Skip to content
Livestream launch event: Duende IdentityServer v7.3 with FAPI 2.0 & new quickstart templates. Register now!

Discovery Endpoint

The client library for the OpenID Connect discovery endpoint is provided as an extension method for HttpClient. The GetDiscoveryDocumentAsync method returns a DiscoveryDocumentResponse object that has both strong and weak typed accessors for the various elements of the discovery document.

You should always check the IsError and Error properties before accessing the contents of the document:

var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://demo.duendesoftware.com");
if (disco.IsError) throw new Exception(disco.Error);

Standard elements can be accessed by using properties:

var tokenEndpoint = disco.TokenEndpoint;
var keys = disco.KeySet.Keys;

Custom elements (or elements not covered by the standard properties) can be accessed like this:

// returns string or null
var stringValue = disco.TryGetString("some_string_element");
// return a nullable boolean
var boolValue = disco.TryGetBoolean("some_boolean_element");
// return array (maybe empty)
var arrayValue = disco.TryGetStringArray("some_array_element");
// returns JToken
var rawJson = disco.TryGetValue("some_element");

By default, the discovery response is validated before it is returned to the client, validation includes:

  • enforce that HTTPS is used (except for localhost addresses)
  • enforce that the issuer matches the authority
  • enforce that the protocol endpoints are on the same DNS name as the authority
  • enforce the existence of a keyset

Policy violation errors will set the ErrorType property on the DiscoveryDocumentResponse to PolicyViolation.

All the standard validation rules can be modified using the DiscoveryPolicy class, e.g. disabling the issuer name check:

var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = "https://demo.duendesoftware.com",
Policy =
{
ValidateIssuerName = false
}
});

When the URIs in the discovery document are on a different base address than the issuer URI, you may encounter the error Endpoint is on a different host than authority. For such scenario, additional endpoint base addresses can be configured:

var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = "https://demo.duendesoftware.com",
Policy =
{
AdditionalEndpointBaseAddresses = [ "https://auth.domain.tld" ]
}
});

You can also customize validation strategy based on the authority with your own implementation of IAuthorityValidationStrategy. By default, comparison uses ordinal string comparison. To switch to Uri comparison:

var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{
Address = "https://demo.duendesoftware.com",
Policy =
{
AuthorityValidationStrategy = new AuthorityUrlValidationStrategy()
}
});

You should periodically update your local copy of the discovery document, to be able to react to configuration changes on the server. This is especially important for playing nice with automatic key rotation.

The DiscoveryCache class can help you with that.

The following code will set up the cache, retrieve the document the first time it is needed, and then cache it for 24 hours:

var cache = new DiscoveryCache("https://demo.duendesoftware.com");

You can then access the document like this:

var disco = await cache.GetAsync();
if (disco.IsError) throw new Exception(disco.Error);

You can specify the cache duration using the CacheDuration property and also specify a custom discovery policy by passing in a DiscoveryPolicy to the constructor.

By default, the discovery cache will create a new instance of HttpClient every time it needs to access the discovery endpoint. You can modify this behavior in two ways, either by passing in a pre-created instance into the constructor, or by providing a function that will return an HttpClient when needed.

The following code will set up the discovery cache in the ASP.NET Core service provider and will use the HttpClientFactory to create clients:

services.AddSingleton<IDiscoveryCache>(r =>
{
var factory = r.GetRequiredService<IHttpClientFactory>();
return new DiscoveryCache(Constants.Authority, () => factory.CreateClient());
});

DiscoveryDocumentResponse Properties Reference

Section titled “DiscoveryDocumentResponse Properties Reference”

The following table lists the standard properties on the DiscoveryDocumentResponse class:

PropertyDescription
PolicyGets or sets the discovery policy used to configure how the discovery document is processed
KeySetGets or sets the JSON Web Key Set (JWKS) associated with the discovery document
MtlsEndpointAliasesGets the mutual TLS (mTLS) endpoint aliases
IssuerGets the issuer identifier for the authorization server
AuthorizeEndpointGets the authorization endpoint URL
TokenEndpointGets token endpoint URL
UserInfoEndpointGets user info endpoint URL
IntrospectionEndpointGets the introspection endpoint URL
RevocationEndpointGets the revocation endpoint URL
DeviceAuthorizationEndpointGets the device authorization endpoint URL
BackchannelAuthenticationEndpointGets the backchannel authentication endpoint URL
JwksUriGets the URI of the JSON Web Key Set (JWKS)
EndSessionEndpointGets the end session endpoint URL
CheckSessionIframeGets the check session iframe URL
RegistrationEndpointGets the dynamic client registration (DCR) endpoint URL
PushedAuthorizationRequestEndpointGets the pushed authorization request (PAR) endpoint URL
FrontChannelLogoutSupportedGets a flag indicating whether front-channel logout is supported
FrontChannelLogoutSessionSupportedGets a flag indicating whether a session ID (sid) parameter is supported at the front-channel logout endpoint
GrantTypesSupportedGets the supported grant types
CodeChallengeMethodsSupportedGets the supported code challenge methods
ScopesSupportedGets the supported scopes
SubjectTypesSupportedGets the supported subject types
ResponseModesSupportedGets the supported response modes
ResponseTypesSupportedGets the supported response types
ClaimsSupportedGets the supported claims
TokenEndpointAuthenticationMethodsSupportedGets the authentication methods supported by the token endpoint
TokenEndpointAuthenticationSigningAlgorithmsSupportedGets the signing algorithms supported by the token endpoint for client authentication
BackchannelTokenDeliveryModesSupportedGets the supported backchannel token delivery modes
BackchannelUserCodeParameterSupportedGets a flag indicating whether the backchannel user code parameter is supported
RequirePushedAuthorizationRequestsGets a flag indicating whether the use of pushed authorization requests (PAR) is required
IntrospectionSigningAlgorithmsSupportedGets the signing algorithms supported for introspection responses
IntrospectionEncryptionAlgorithmsSupportedGets the encryption “alg” values supported for encrypted JWT introspection responses
IntrospectionEncryptionEncValuesSupportedGets the encryption “enc” values supported for encrypted JWT introspection responses
ScopesThe list of scopes associated to the token or an empty array if no scope claim is present
ClientIdThe client identifier for the OAuth 2.0 client that requested the token or null if the client_id claim is missing
UserNameThe human-readable identifier for the resource owner who authorized the token or null if the username claim is missing
TokenTypeThe type of the token as defined in section 5.1 of OAuth 2.0 (RFC6749) or null if the token_type claim is missing
ExpirationThe expiration time of the token or null if the exp claim is missing
IssuedAtThe issuance time of the token or null if the iat claim is missing
NotBeforeThe validity start time of the token or null if the nbf claim is missing
SubjectThe subject of the token or null if the sub claim is missing
AudiencesThe service-specific list of string identifiers representing the intended audience for the token or an empty array if no aud claim is present
IssuerThe string representing the issuer of the token or null if the iss claim is missing
JwtIdThe string identifier for the token or null if the jti claim is missing