private async Task <IEndpointResult> ExecuteDiscoDocAsync(HttpContext context) { _logger.LogTrace("Start discovery request"); var baseUrl = _context.GetIdentityServerBaseUrl().EnsureTrailingSlash(); var allScopes = await _scopes.GetScopesAsync(publicOnly : true); var showScopes = new List <Scope>(); var document = new DiscoveryDocument { issuer = _context.GetIssuerUri(), subject_types_supported = new[] { "public" }, id_token_signing_alg_values_supported = new[] { Constants.SigningAlgorithms.RSA_SHA_256 } }; // scopes if (_options.DiscoveryOptions.ShowIdentityScopes) { showScopes.AddRange(allScopes.Where(s => s.Type == ScopeType.Identity)); } if (_options.DiscoveryOptions.ShowResourceScopes) { showScopes.AddRange(allScopes.Where(s => s.Type == ScopeType.Resource)); } if (showScopes.Any()) { document.scopes_supported = showScopes.Where(s => s.ShowInDiscoveryDocument).Select(s => s.Name).ToArray(); } // claims if (_options.DiscoveryOptions.ShowClaims) { var claims = new List <string>(); foreach (var s in allScopes) { claims.AddRange(from c in s.Claims where s.Type == ScopeType.Identity select c.Name); } document.claims_supported = claims.Distinct().ToArray(); } // grant types if (_options.DiscoveryOptions.ShowGrantTypes) { var standardGrantTypes = Constants.SupportedGrantTypes.AsEnumerable(); if (this._options.AuthenticationOptions.EnableLocalLogin == false) { standardGrantTypes = standardGrantTypes.Where(type => type != OidcConstants.GrantTypes.Password); } var showGrantTypes = new List <string>(standardGrantTypes); if (_options.DiscoveryOptions.ShowCustomGrantTypes) { showGrantTypes.AddRange(_customGrants.GetAvailableGrantTypes()); } document.grant_types_supported = showGrantTypes.ToArray(); } // response types if (_options.DiscoveryOptions.ShowResponseTypes) { document.response_types_supported = Constants.SupportedResponseTypes.ToArray(); } // response modes if (_options.DiscoveryOptions.ShowResponseModes) { document.response_modes_supported = Constants.SupportedResponseModes.ToArray(); } // token endpoint authentication methods if (_options.DiscoveryOptions.ShowTokenEndpointAuthenticationMethods) { document.token_endpoint_auth_methods_supported = _parsers.GetAvailableAuthenticationMethods().ToArray(); } // endpoints if (_options.DiscoveryOptions.ShowEndpoints) { if (_options.Endpoints.EnableEndSessionEndpoint) { document.http_logout_supported = true; } if (_options.Endpoints.EnableAuthorizeEndpoint) { document.authorization_endpoint = baseUrl + Constants.RoutePaths.Oidc.Authorize; } if (_options.Endpoints.EnableTokenEndpoint) { document.token_endpoint = baseUrl + Constants.RoutePaths.Oidc.Token; } if (_options.Endpoints.EnableUserInfoEndpoint) { document.userinfo_endpoint = baseUrl + Constants.RoutePaths.Oidc.UserInfo; } if (_options.Endpoints.EnableEndSessionEndpoint) { document.end_session_endpoint = baseUrl + Constants.RoutePaths.Oidc.EndSession; } if (_options.Endpoints.EnableCheckSessionEndpoint) { document.check_session_iframe = baseUrl + Constants.RoutePaths.Oidc.CheckSession; } if (_options.Endpoints.EnableTokenRevocationEndpoint) { document.revocation_endpoint = baseUrl + Constants.RoutePaths.Oidc.Revocation; } if (_options.Endpoints.EnableIntrospectionEndpoint) { document.introspection_endpoint = baseUrl + Constants.RoutePaths.Oidc.Introspection; } } if (_options.DiscoveryOptions.ShowKeySet) { if (_options.SigningCertificate != null) { document.jwks_uri = baseUrl + Constants.RoutePaths.Oidc.DiscoveryWebKeys; } } return(new DiscoveryDocumentResult(document, _options.DiscoveryOptions.CustomEntries)); }
private async Task <IEndpointResult> ExecuteDiscoDocAsync(HttpContext context) { _logger.LogDebug("Start discovery request"); if (!_options.Endpoints.EnableDiscoveryEndpoint) { _logger.LogInformation("Discovery endpoint disabled. 404."); return(new StatusCodeResult(404)); } var baseUrl = context.GetIdentityServerBaseUrl().EnsureTrailingSlash(); var allScopes = await _scopes.GetEnabledScopesAsync(publicOnly : true); var showScopes = new List <Scope>(); var document = new DiscoveryDocument { issuer = context.GetIssuerUri(), subject_types_supported = new[] { "public" }, id_token_signing_alg_values_supported = new[] { Constants.SigningAlgorithms.RSA_SHA_256 }, code_challenge_methods_supported = new[] { OidcConstants.CodeChallengeMethods.Plain, OidcConstants.CodeChallengeMethods.Sha256 } }; // scopes var theScopes = allScopes as Scope[] ?? allScopes.ToArray(); if (_options.DiscoveryOptions.ShowIdentityScopes) { showScopes.AddRange(theScopes.Where(s => s.Type == ScopeType.Identity)); } if (_options.DiscoveryOptions.ShowResourceScopes) { showScopes.AddRange(theScopes.Where(s => s.Type == ScopeType.Resource)); } if (showScopes.Any()) { document.scopes_supported = showScopes.Where(s => s.ShowInDiscoveryDocument).Select(s => s.Name).ToArray(); } // claims if (_options.DiscoveryOptions.ShowClaims) { var claims = new List <string>(); foreach (var s in theScopes) { claims.AddRange(from c in s.Claims where s.Type == ScopeType.Identity select c.Name); } document.claims_supported = claims.Distinct().ToArray(); } // grant types if (_options.DiscoveryOptions.ShowGrantTypes) { var standardGrantTypes = new List <string> { OidcConstants.GrantTypes.AuthorizationCode, OidcConstants.GrantTypes.ClientCredentials, OidcConstants.GrantTypes.RefreshToken, OidcConstants.GrantTypes.Implicit }; if (!(_resourceOwnerValidator is NotSupportedResouceOwnerPasswordValidator)) { standardGrantTypes.Add(OidcConstants.GrantTypes.Password); } var showGrantTypes = new List <string>(standardGrantTypes); if (_options.DiscoveryOptions.ShowExtensionGrantTypes) { showGrantTypes.AddRange(_extensionGrants.GetAvailableGrantTypes()); } document.grant_types_supported = showGrantTypes.ToArray(); } // response types if (_options.DiscoveryOptions.ShowResponseTypes) { document.response_types_supported = Constants.SupportedResponseTypes.ToArray(); } // response modes if (_options.DiscoveryOptions.ShowResponseModes) { document.response_modes_supported = Constants.SupportedResponseModes.ToArray(); } // token endpoint authentication methods if (_options.DiscoveryOptions.ShowTokenEndpointAuthenticationMethods) { document.token_endpoint_auth_methods_supported = _parsers.GetAvailableAuthenticationMethods().ToArray(); } // endpoints if (_options.DiscoveryOptions.ShowEndpoints) { if (_options.Endpoints.EnableAuthorizeEndpoint) { document.authorization_endpoint = baseUrl + Constants.ProtocolRoutePaths.Authorize; } if (_options.Endpoints.EnableTokenEndpoint) { document.token_endpoint = baseUrl + Constants.ProtocolRoutePaths.Token; } if (_options.Endpoints.EnableUserInfoEndpoint) { document.userinfo_endpoint = baseUrl + Constants.ProtocolRoutePaths.UserInfo; } if (_options.Endpoints.EnableEndSessionEndpoint) { document.frontchannel_logout_session_supported = true; document.frontchannel_logout_supported = true; document.end_session_endpoint = baseUrl + Constants.ProtocolRoutePaths.EndSession; } if (_options.Endpoints.EnableCheckSessionEndpoint) { document.check_session_iframe = baseUrl + Constants.ProtocolRoutePaths.CheckSession; } if (_options.Endpoints.EnableTokenRevocationEndpoint) { document.revocation_endpoint = baseUrl + Constants.ProtocolRoutePaths.Revocation; } if (_options.Endpoints.EnableIntrospectionEndpoint) { document.introspection_endpoint = baseUrl + Constants.ProtocolRoutePaths.Introspection; } } if (_options.DiscoveryOptions.ShowKeySet) { if ((await _keys.GetValidationKeysAsync()).Any()) { document.jwks_uri = baseUrl + Constants.ProtocolRoutePaths.DiscoveryWebKeys; } } return(new DiscoveryDocumentResult(document, _options.DiscoveryOptions.CustomEntries)); }
/// <summary> /// Creates the discovery document. /// </summary> /// <param name="baseUrl">The base URL.</param> /// <param name="issuerUri">The issuer URI.</param> public virtual async Task <Dictionary <string, object> > CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri) { var entries = new Dictionary <string, object>(); // issuer entries.Add(OidcConstants.Discovery.Issuer, issuerUri); // jwks if (Options.Discovery.ShowKeySet) { if ((await Keys.GetValidationKeysAsync()).Any()) { entries.Add(OidcConstants.Discovery.JwksUri, baseUrl + Constants.ProtocolRoutePaths.DiscoveryWebKeys); } } // endpoints if (Options.Discovery.ShowEndpoints) { if (Options.Endpoints.EnableAuthorizeEndpoint) { entries.Add(OidcConstants.Discovery.AuthorizationEndpoint, baseUrl + Constants.ProtocolRoutePaths.Authorize); } if (Options.Endpoints.EnableTokenEndpoint) { entries.Add(OidcConstants.Discovery.TokenEndpoint, baseUrl + Constants.ProtocolRoutePaths.Token); } if (Options.Endpoints.EnableUserInfoEndpoint) { entries.Add(OidcConstants.Discovery.UserInfoEndpoint, baseUrl + Constants.ProtocolRoutePaths.UserInfo); } if (Options.Endpoints.EnableEndSessionEndpoint) { entries.Add(OidcConstants.Discovery.EndSessionEndpoint, baseUrl + Constants.ProtocolRoutePaths.EndSession); } if (Options.Endpoints.EnableCheckSessionEndpoint) { entries.Add(OidcConstants.Discovery.CheckSessionIframe, baseUrl + Constants.ProtocolRoutePaths.CheckSession); } if (Options.Endpoints.EnableTokenRevocationEndpoint) { entries.Add(OidcConstants.Discovery.RevocationEndpoint, baseUrl + Constants.ProtocolRoutePaths.Revocation); } if (Options.Endpoints.EnableIntrospectionEndpoint) { entries.Add(OidcConstants.Discovery.IntrospectionEndpoint, baseUrl + Constants.ProtocolRoutePaths.Introspection); } } // logout if (Options.Endpoints.EnableEndSessionEndpoint) { entries.Add(OidcConstants.Discovery.FrontChannelLogoutSupported, true); entries.Add(OidcConstants.Discovery.FrontChannelLogoutSessionSupported, true); } // scopes and claims if (Options.Discovery.ShowIdentityScopes || Options.Discovery.ShowApiScopes || Options.Discovery.ShowClaims) { var resources = await ResourceStore.GetAllEnabledResourcesAsync(); var scopes = new List <string>(); // scopes if (Options.Discovery.ShowIdentityScopes) { scopes.AddRange(resources.IdentityResources.Where(x => x.ShowInDiscoveryDocument).Select(x => x.Name)); } if (Options.Discovery.ShowApiScopes) { var apiScopes = from api in resources.ApiResources from scope in api.Scopes where scope.ShowInDiscoveryDocument select scope.Name; scopes.AddRange(apiScopes); scopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } if (scopes.Any()) { entries.Add(OidcConstants.Discovery.ScopesSupported, scopes.ToArray()); } // claims if (Options.Discovery.ShowClaims) { var claims = new List <string>(); claims.AddRange(resources.IdentityResources.SelectMany(x => x.UserClaims)); claims.AddRange(resources.ApiResources.SelectMany(x => x.UserClaims)); entries.Add(OidcConstants.Discovery.ClaimsSupported, claims.Distinct().ToArray()); } } // grant types if (Options.Discovery.ShowGrantTypes) { var standardGrantTypes = new List <string> { OidcConstants.GrantTypes.AuthorizationCode, OidcConstants.GrantTypes.ClientCredentials, OidcConstants.GrantTypes.RefreshToken, OidcConstants.GrantTypes.Implicit }; if (!(ResourceOwnerValidator is NotSupportedResouceOwnerPasswordValidator)) { standardGrantTypes.Add(OidcConstants.GrantTypes.Password); } var showGrantTypes = new List <string>(standardGrantTypes); if (Options.Discovery.ShowExtensionGrantTypes) { showGrantTypes.AddRange(ExtensionGrants.GetAvailableGrantTypes()); } entries.Add(OidcConstants.Discovery.GrantTypesSupported, showGrantTypes.ToArray()); } // response types if (Options.Discovery.ShowResponseTypes) { entries.Add(OidcConstants.Discovery.ResponseTypesSupported, Constants.SupportedResponseTypes.ToArray()); } // response modes if (Options.Discovery.ShowResponseModes) { entries.Add(OidcConstants.Discovery.ResponseModesSupported, Constants.SupportedResponseModes.ToArray()); } // misc if (Options.Discovery.ShowTokenEndpointAuthenticationMethods) { entries.Add(OidcConstants.Discovery.TokenEndpointAuthenticationMethodsSupported, SecretParsers.GetAvailableAuthenticationMethods().ToArray()); } entries.Add(OidcConstants.Discovery.SubjectTypesSupported, new[] { "public" }); entries.Add(OidcConstants.Discovery.IdTokenSigningAlgorithmsSupported, new[] { Constants.SigningAlgorithms.RSA_SHA_256 }); entries.Add(OidcConstants.Discovery.CodeChallengeMethodsSupported, new[] { OidcConstants.CodeChallengeMethods.Plain, OidcConstants.CodeChallengeMethods.Sha256 }); // custom entries if (!Options.Discovery.CustomEntries.IsNullOrEmpty()) { foreach (var customEntry in Options.Discovery.CustomEntries) { if (entries.ContainsKey(customEntry.Key)) { Logger.LogError("Discovery custom entry {key} cannot be added, because it already exists.", customEntry.Key); } else { var customValueString = customEntry.Value as string; if (customValueString != null) { if (customValueString.StartsWith("~/") && Options.Discovery.ExpandRelativePathsInCustomEntries) { entries.Add(customEntry.Key, baseUrl + customValueString.Substring(2)); continue; } } entries.Add(customEntry.Key, customEntry.Value); } } } return(entries); }
/// <summary> /// Creates the discovery document. /// </summary> /// <param name="baseUrl">The base URL.</param> /// <param name="issuerUri">The issuer URI.</param> public virtual async Task <Dictionary <string, object> > CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri) { var entries = new Dictionary <string, object> { { OidcConstants.Discovery.Issuer, issuerUri } }; // jwks if (Options.Discovery.ShowKeySet) { if ((await Keys.GetValidationKeysAsync()).Any()) { entries.Add(OidcConstants.Discovery.JwksUri, baseUrl + Constants.ProtocolRoutePaths.DiscoveryWebKeys); } } // endpoints if (Options.Discovery.ShowEndpoints) { if (Options.Endpoints.EnableAuthorizeEndpoint) { entries.Add(OidcConstants.Discovery.AuthorizationEndpoint, baseUrl + Constants.ProtocolRoutePaths.Authorize); } if (Options.Endpoints.EnableTokenEndpoint) { entries.Add(OidcConstants.Discovery.TokenEndpoint, baseUrl + Constants.ProtocolRoutePaths.Token); } if (Options.Endpoints.EnableUserInfoEndpoint) { entries.Add(OidcConstants.Discovery.UserInfoEndpoint, baseUrl + Constants.ProtocolRoutePaths.UserInfo); } if (Options.Endpoints.EnableEndSessionEndpoint) { entries.Add(OidcConstants.Discovery.EndSessionEndpoint, baseUrl + Constants.ProtocolRoutePaths.EndSession); } if (Options.Endpoints.EnableCheckSessionEndpoint) { entries.Add(OidcConstants.Discovery.CheckSessionIframe, baseUrl + Constants.ProtocolRoutePaths.CheckSession); } if (Options.Endpoints.EnableTokenRevocationEndpoint) { entries.Add(OidcConstants.Discovery.RevocationEndpoint, baseUrl + Constants.ProtocolRoutePaths.Revocation); } if (Options.Endpoints.EnableIntrospectionEndpoint) { entries.Add(OidcConstants.Discovery.IntrospectionEndpoint, baseUrl + Constants.ProtocolRoutePaths.Introspection); } if (Options.Endpoints.EnableDeviceAuthorizationEndpoint) { entries.Add(OidcConstants.Discovery.DeviceAuthorizationEndpoint, baseUrl + Constants.ProtocolRoutePaths.DeviceAuthorization); } if (Options.MutualTls.Enabled) { var mtlsEndpoints = new Dictionary <string, string>(); if (Options.Endpoints.EnableTokenEndpoint) { mtlsEndpoints.Add(OidcConstants.Discovery.TokenEndpoint, ConstructMtlsEndpoint(Constants.ProtocolRoutePaths.Token)); } if (Options.Endpoints.EnableTokenRevocationEndpoint) { mtlsEndpoints.Add(OidcConstants.Discovery.RevocationEndpoint, ConstructMtlsEndpoint(Constants.ProtocolRoutePaths.Revocation)); } if (Options.Endpoints.EnableIntrospectionEndpoint) { mtlsEndpoints.Add(OidcConstants.Discovery.IntrospectionEndpoint, ConstructMtlsEndpoint(Constants.ProtocolRoutePaths.Introspection)); } if (Options.Endpoints.EnableDeviceAuthorizationEndpoint) { mtlsEndpoints.Add(OidcConstants.Discovery.DeviceAuthorizationEndpoint, ConstructMtlsEndpoint(Constants.ProtocolRoutePaths.DeviceAuthorization)); } if (mtlsEndpoints.Any()) { entries.Add(OidcConstants.Discovery.MtlsEndpointAliases, mtlsEndpoints); } string ConstructMtlsEndpoint(string endpoint) { // path based if (Options.MutualTls.DomainName.IsMissing()) { return(baseUrl + endpoint.Replace(Constants.ProtocolRoutePaths.ConnectPathPrefix, Constants.ProtocolRoutePaths.MtlsPathPrefix)); } else { // domain based if (Options.MutualTls.DomainName.Contains(".")) { return($"https://{Options.MutualTls.DomainName}/{endpoint}"); } // sub-domain based else { var parts = baseUrl.Split("://"); return($"https://{Options.MutualTls.DomainName}.{parts[1]}{endpoint}"); } } } } } // logout if (Options.Endpoints.EnableEndSessionEndpoint) { entries.Add(OidcConstants.Discovery.FrontChannelLogoutSupported, true); entries.Add(OidcConstants.Discovery.FrontChannelLogoutSessionSupported, true); entries.Add(OidcConstants.Discovery.BackChannelLogoutSupported, true); entries.Add(OidcConstants.Discovery.BackChannelLogoutSessionSupported, true); } // scopes and claims if (Options.Discovery.ShowIdentityScopes || Options.Discovery.ShowApiScopes || Options.Discovery.ShowClaims) { var resources = await ResourceStore.GetAllEnabledResourcesAsync(); var scopes = new List <string>(); // scopes if (Options.Discovery.ShowIdentityScopes) { scopes.AddRange(resources.IdentityResources.Where(x => x.ShowInDiscoveryDocument).Select(x => x.Name)); } if (Options.Discovery.ShowApiScopes) { var apiScopes = from api in resources.ApiResources from scope in api.Scopes where scope.ShowInDiscoveryDocument select scope.Name; scopes.AddRange(apiScopes); scopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } if (scopes.Any()) { entries.Add(OidcConstants.Discovery.ScopesSupported, scopes.ToArray()); } // claims if (Options.Discovery.ShowClaims) { var claims = new List <string>(); // add non-hidden identity scopes related claims claims.AddRange(resources.IdentityResources.Where(x => x.ShowInDiscoveryDocument).SelectMany(x => x.UserClaims)); // add non-hidden api scopes related claims foreach (var resource in resources.ApiResources) { claims.AddRange(resource.UserClaims); foreach (var scope in resource.Scopes) { if (scope.ShowInDiscoveryDocument) { claims.AddRange(scope.UserClaims); } } } entries.Add(OidcConstants.Discovery.ClaimsSupported, claims.Distinct().ToArray()); } } // grant types if (Options.Discovery.ShowGrantTypes) { var standardGrantTypes = new List <string> { OidcConstants.GrantTypes.AuthorizationCode, OidcConstants.GrantTypes.ClientCredentials, OidcConstants.GrantTypes.RefreshToken, OidcConstants.GrantTypes.Implicit }; if (!(ResourceOwnerValidator is NotSupportedResourceOwnerPasswordValidator)) { standardGrantTypes.Add(OidcConstants.GrantTypes.Password); } if (Options.Endpoints.EnableDeviceAuthorizationEndpoint) { standardGrantTypes.Add(OidcConstants.GrantTypes.DeviceCode); } var showGrantTypes = new List <string>(standardGrantTypes); if (Options.Discovery.ShowExtensionGrantTypes) { showGrantTypes.AddRange(ExtensionGrants.GetAvailableGrantTypes()); } entries.Add(OidcConstants.Discovery.GrantTypesSupported, showGrantTypes.ToArray()); } // response types if (Options.Discovery.ShowResponseTypes) { entries.Add(OidcConstants.Discovery.ResponseTypesSupported, Constants.SupportedResponseTypes.ToArray()); } // response modes if (Options.Discovery.ShowResponseModes) { entries.Add(OidcConstants.Discovery.ResponseModesSupported, Constants.SupportedResponseModes.ToArray()); } // misc if (Options.Discovery.ShowTokenEndpointAuthenticationMethods) { var types = SecretParsers.GetAvailableAuthenticationMethods().ToList(); if (Options.MutualTls.Enabled) { types.Add(OidcConstants.EndpointAuthenticationMethods.TlsClientAuth); types.Add(OidcConstants.EndpointAuthenticationMethods.SelfSignedTlsClientAuth); } entries.Add(OidcConstants.Discovery.TokenEndpointAuthenticationMethodsSupported, types); } var signingCredentials = await Keys.GetAllSigningCredentialsAsync(); if (signingCredentials.Any()) { entries.Add(OidcConstants.Discovery.IdTokenSigningAlgorithmsSupported, new[] { signingCredentials.Select(c => c.Algorithm).Distinct() }); } entries.Add(OidcConstants.Discovery.SubjectTypesSupported, new[] { "public" }); entries.Add(OidcConstants.Discovery.CodeChallengeMethodsSupported, new[] { OidcConstants.CodeChallengeMethods.Plain, OidcConstants.CodeChallengeMethods.Sha256 }); if (Options.Endpoints.EnableAuthorizeEndpoint) { entries.Add(OidcConstants.Discovery.RequestParameterSupported, true); if (Options.Endpoints.EnableJwtRequestUri) { entries.Add(OidcConstants.Discovery.RequestUriParameterSupported, true); } } if (Options.MutualTls.Enabled) { entries.Add(OidcConstants.Discovery.TlsClientCertificateBoundAccessTokens, true); } // custom entries if (!Options.Discovery.CustomEntries.IsNullOrEmpty()) { foreach (var customEntry in Options.Discovery.CustomEntries) { if (entries.ContainsKey(customEntry.Key)) { Logger.LogError("Discovery custom entry {key} cannot be added, because it already exists.", customEntry.Key); } else { if (customEntry.Value is string customValueString) { if (customValueString.StartsWith("~/") && Options.Discovery.ExpandRelativePathsInCustomEntries) { entries.Add(customEntry.Key, baseUrl + customValueString.Substring(2)); continue; } } entries.Add(customEntry.Key, customEntry.Value); } } } return(entries); }