Example #1
0
        public async Task <string> ProcessAsync(ExtendTokenRequest extendTokenRequest)
        {
            _logger.LogDebug("Creating new JWT Token");
            var handler = new JwtSecurityTokenHandler();
            //Receieved JWT Token
            var rjwt   = handler.ReadJwtToken(extendTokenRequest.JWTTokenRAW);
            var claims = rjwt.Claims.ToList();

            //Adding the new api scope to token
            foreach (var aud in extendTokenRequest.AudiencesToAdd)
            {
                //If claims exist, do not add
                var isClaimExists = claims.Where(c => c.Type == "aud" && c.Value == aud).Any();
                if (!isClaimExists)
                {
                    claims.Add(new Claim("aud", aud));
                }
            }

            //Getting keys
            var validationKey = _keys.GetValidationKeysAsync().Result.FirstOrDefault();
            var credentials   = new SigningCredentials(validationKey, "RS256");

            JwtSecurityToken newjwt = new JwtSecurityToken(rjwt.Issuer,
                                                           null,
                                                           claims,
                                                           rjwt.ValidFrom,
                                                           rjwt.ValidTo,
                                                           credentials);

            var signedAndEncodedToken = handler.WriteToken(newjwt);

            return(signedAndEncodedToken);
        }
Example #2
0
        /// <summary>
        /// Validates the JWT for Signature.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public async Task <TokenValidationResult> ValidateJWTSignature(string jwttoken)
        {
            //Validate JWT Token
            SecurityToken securityToken = null;

            var handler = new JwtSecurityTokenHandler();

            handler.InboundClaimTypeMap.Clear();
            var validationKey = _keys.GetValidationKeysAsync().Result.FirstOrDefault();
            TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                ValidateIssuer           = false,
                ValidateAudience         = false,
                IssuerSigningKey         = validationKey
            };

            try
            {
                var id = handler.ValidateToken(jwttoken, validationParameters,
                                               out securityToken);
            }
            catch (Exception ex)
            {
                return(new TokenValidationResult
                {
                    IsError = true,
                    Error = ex.Message
                });
            }
            return(new TokenValidationResult
            {
                IsError = false,
                Jwt = jwttoken
            });
        }
Example #3
0
        public virtual async Task <TokenValidationResult> ValidateIdentityTokenAsync(string token, string clientId = null, bool validateLifetime = true)
        {
            _logger.LogDebug("Start identity token validation");

            if (token.Length > _options.InputLengthRestrictions.Jwt)
            {
                _logger.LogError("JWT too long");
                return(Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken));
            }

            if (clientId.IsMissing())
            {
                clientId = GetClientIdFromJwt(token);

                if (clientId.IsMissing())
                {
                    _logger.LogError("No clientId supplied, can't find id in identity token.");
                    return(Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken));
                }
            }

            _log.ClientId         = clientId;
            _log.ValidateLifetime = validateLifetime;

            var client = await _clients.FindEnabledClientByIdAsync(clientId);

            if (client == null)
            {
                _logger.LogError("Unknown or diabled client: {clientId}.", clientId);
                return(Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken));
            }

            _log.ClientName = client.ClientName;
            _logger.LogDebug("Client found: {clientId} / {clientName}", client.ClientId, client.ClientName);

            var keys = await _keys.GetValidationKeysAsync();

            var result = await ValidateJwtAsync(token, clientId, keys, validateLifetime);

            result.Client = client;

            if (result.IsError)
            {
                LogError("Error validating JWT");
                return(result);
            }

            _log.Claims = result.Claims.ToClaimsDictionary();

            _logger.LogDebug("Calling into custom token validator: {type}", _customValidator.GetType().FullName);
            var customResult = await _customValidator.ValidateIdentityTokenAsync(result);

            if (customResult.IsError)
            {
                LogError("Custom validator failed: " + (customResult.Error ?? "unknown"));
                return(customResult);
            }

            _log.Claims = customResult.Claims.ToClaimsDictionary();

            LogSuccess();
            return(customResult);
        }
Example #4
0
        public async Task <TokenValidationResult> ValidateIdentityTokenAsync(string token, string clientId = null, bool validateLifetime = true)
        {
            _logger.LogDebug("Start identity token validation");

            if (token.Length > _options.InputLengthRestrictions.Jwt)
            {
                _logger.LogError("JWT too long");
                return(Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken));
            }

            if (clientId.IsMissing())
            {
                clientId = GetClientIdFromJwt(token);

                if (clientId.IsMissing())
                {
                    _logger.LogError("No clientId supplied, can't find id in identity token.");
                    return(Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken));
                }
            }

            _log.ClientId         = clientId;
            _log.ValidateLifetime = validateLifetime;

            var client = await _clients.FindEnabledClientByIdAsync(clientId);

            if (client == null)
            {
                _logger.LogError("Unknown or disabled client: {clientId}.", clientId);
                return(Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken));
            }

            _log.ClientName = client.ClientName;
            _logger.LogDebug("Client found: {clientId} / {clientName}", client.ClientId, client.ClientName);

            var keys = await _keys.GetValidationKeysAsync();

            var result = await ValidateJwtAsync(token, clientId, keys, validateLifetime);

            result.Client = client;

            if (result.IsError)
            {
                LogError("Error validating JWT");
                return(result);
            }

            _log.Claims = result.Claims.ToClaimsDictionary();

            // make sure user is still active (if sub claim is present)
            var subClaim = result.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.Subject);

            if (subClaim != null)
            {
                var principal = Principal.Create("tokenvalidator", result.Claims.ToArray());

                var isActiveCtx = new IsActiveContext(principal, result.Client, IdentityServerConstants.ProfileIsActiveCallers.IdentityTokenValidation);
                await _profile.IsActiveAsync(isActiveCtx);

                if (isActiveCtx.IsActive == false)
                {
                    _logger.LogError("User marked as not active: {subject}", subClaim.Value);

                    result.IsError = true;
                    result.Error   = OidcConstants.ProtectedResourceErrors.InvalidToken;
                    result.Claims  = null;

                    return(result);
                }
            }

            _logger.LogDebug("Calling into custom token validator: {type}", _customValidator.GetType().FullName);
            var customResult = await _customValidator.ValidateIdentityTokenAsync(result);

            if (customResult.IsError)
            {
                LogError("Custom validator failed: " + (customResult.Error ?? "unknown"));
                return(customResult);
            }

            _log.Claims = customResult.Claims.ToClaimsDictionary();

            LogSuccess();
            return(customResult);
        }
        /// <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));
                        }

                        // 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 scope in resources.ApiScopes
                                    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));
                    claims.AddRange(resources.ApiResources.Where(x => x.ShowInDiscoveryDocument).SelectMany(x => x.UserClaims));
                    claims.AddRange(resources.ApiScopes.Where(x => x.ShowInDiscoveryDocument).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 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())
            {
                var signingAlgorithms = signingCredentials.Select(c => c.Algorithm).Distinct();
                entries.Add(OidcConstants.Discovery.IdTokenSigningAlgorithmsSupported, signingAlgorithms);
            }

            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);

                entries.Add(OidcConstants.Discovery.RequestObjectSigningAlgorithmsSupported, new[]
                {
                    SecurityAlgorithms.RsaSha256,
                    SecurityAlgorithms.RsaSha384,
                    SecurityAlgorithms.RsaSha512,

                    SecurityAlgorithms.RsaSsaPssSha256,
                    SecurityAlgorithms.RsaSsaPssSha384,
                    SecurityAlgorithms.RsaSsaPssSha512,

                    SecurityAlgorithms.EcdsaSha256,
                    SecurityAlgorithms.EcdsaSha384,
                    SecurityAlgorithms.EcdsaSha512,

                    SecurityAlgorithms.HmacSha256,
                    SecurityAlgorithms.HmacSha384,
                    SecurityAlgorithms.HmacSha512
                });

                if (Options.Endpoints.EnableJwtRequestUri)
                {
                    entries.Add(OidcConstants.Discovery.RequestUriParameterSupported, true);
                }
            }

            entries.Add(OidcConstants.Discovery.AuthorizationResponseIssParameterSupported, true);

            if (Options.MutualTls.Enabled)
            {
                entries.Add(OidcConstants.Discovery.TlsClientCertificateBoundAccessTokens, true);
            }

            // custom entries
            if (!Options.Discovery.CustomEntries.IsNullOrEmpty())
            {
                foreach ((string key, object value) in Options.Discovery.CustomEntries)
                {
                    if (entries.ContainsKey(key))
                    {
                        Logger.LogError("Discovery custom entry {key} cannot be added, because it already exists.", key);
                    }
                    else
                    {
                        if (value is string customValueString)
                        {
                            if (customValueString.StartsWith("~/") && Options.Discovery.ExpandRelativePathsInCustomEntries)
                            {
                                entries.Add(key, baseUrl + customValueString.Substring(2));
                                continue;
                            }
                        }

                        entries.Add(key, 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>();

            // 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);
        }
        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));
        }