/// <inheritdoc/>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });

            if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.X509Certificate)
            {
                _logger.LogDebug("X509 thumbprint secret validator cannot process {type}", parsedSecret.Type ?? "null");
                return(fail);
            }

            if (!(parsedSecret.Credential is X509Certificate2 cert))
            {
                throw new InvalidOperationException("Credential is not a x509 certificate.");
            }

            var thumbprint = cert.Thumbprint;

            if (thumbprint == null)
            {
                _logger.LogWarning("No thumbprint found in X509 certificate.");
                return(fail);
            }

            var thumbprintSecrets = secrets.Where(s => s.Type == IdentityServerConstants.SecretTypes.X509CertificateThumbprint);

            if (!thumbprintSecrets.Any())
            {
                _logger.LogDebug("No thumbprint secrets configured for client.");
                return(fail);
            }

            foreach (var thumbprintSecret in thumbprintSecrets)
            {
                var secretDescription = string.IsNullOrEmpty(thumbprintSecret.Description) ? "no description" : thumbprintSecret.Description;

                if (thumbprint.Equals(thumbprintSecret.Value, StringComparison.OrdinalIgnoreCase))
                {
                    var result = new SecretValidationResult
                    {
                        Success      = true,
                        Confirmation = cert.CreateThumbprintCnf()
                    };

                    return(Task.FromResult(result));
                }
            }

            _logger.LogDebug("No matching x509 thumbprint secret found.");
            return(fail);
        }
        /// <inheritdoc/>
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = Task.FromResult(new SecretValidationResult {
                Success = false
            });

            if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.X509Certificate)
            {
                _logger.LogDebug("X509 name secret validator cannot process {type}", parsedSecret.Type ?? "null");
                return(fail);
            }

            if (!(parsedSecret.Credential is X509Certificate2 cert))
            {
                throw new InvalidOperationException("Credential is not a x509 certificate.");
            }

            var name = cert.Subject;

            if (name == null)
            {
                _logger.LogWarning("No subject/name found in X509 certificate.");
                return(fail);
            }

            var nameSecrets = secrets.Where(s => s.Type == IdentityServerConstants.SecretTypes.X509CertificateName);

            if (!nameSecrets.Any())
            {
                _logger.LogDebug("No x509 name secrets configured for client.");
                return(fail);
            }

            foreach (var nameSecret in nameSecrets)
            {
                if (name.Equals(nameSecret.Value, StringComparison.Ordinal))
                {
                    var result = new SecretValidationResult
                    {
                        Success      = true,
                        Confirmation = cert.CreateThumbprintCnf()
                    };

                    return(Task.FromResult(result));
                }
            }

            _logger.LogDebug("No matching x509 name secret found.");
            return(fail);
        }
        /// <summary>
        /// Validates the current request.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public async Task <ClientSecretValidationResult> ValidateAsync(HttpContext context)
        {
            _logger.LogDebug("Start client validation");

            var fail = new ClientSecretValidationResult
            {
                IsError = true
            };

            var parsedSecret = await _parser.ParseAsync(context);

            if (parsedSecret == null)
            {
                await RaiseFailureEventAsync("unknown", "No client id found");

                _logger.LogError("No client identifier found");
                return(fail);
            }

            // load client
            var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id);

            if (client == null)
            {
                await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client");

                _logger.LogError("No client with id '{clientId}' found. aborting", parsedSecret.Id);
                return(fail);
            }

            SecretValidationResult secretValidationResult = null;

            if (!client.RequireClientSecret || client.IsImplicitOnly())
            {
                _logger.LogDebug("Public Client - skipping secret validation success");
            }
            else
            {
                secretValidationResult = await _validator.ValidateAsync(client.ClientSecrets, parsedSecret);

                if (secretValidationResult.Success == false)
                {
                    await RaiseFailureEventAsync(client.ClientId, "Invalid client secret");

                    _logger.LogError("Client secret validation failed for client: {clientId}.", client.ClientId);

                    return(fail);
                }
            }

            _logger.LogDebug("Client validation success");

            var success = new ClientSecretValidationResult
            {
                IsError      = false,
                Client       = client,
                Secret       = parsedSecret,
                Confirmation = secretValidationResult?.Confirmation
            };

            await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type);

            return(success);
        }
        /// <summary>
        /// Validates a secret
        /// </summary>
        /// <param name="secrets">The stored secrets.</param>
        /// <param name="parsedSecret">The received secret.</param>
        /// <returns>
        /// A validation result
        /// </returns>
        /// <exception cref="System.ArgumentException">ParsedSecret.Credential is not a JWT token</exception>
        public async Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            var fail = new SecretValidationResult {
                Success = false
            };
            var success = new SecretValidationResult {
                Success = true
            };

            if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.JwtBearer)
            {
                return(fail);
            }

            if (!(parsedSecret.Credential is string jwtTokenString))
            {
                _logger.LogError("ParsedSecret.Credential is not a string.");
                return(fail);
            }

            List <SecurityKey> trustedKeys;

            try
            {
                trustedKeys = await secrets.GetKeysAsync();
            }
            catch (Exception e)
            {
                _logger.LogError(e, "Could not parse secrets");
                return(fail);
            }

            if (!trustedKeys.Any())
            {
                _logger.LogError("There are no keys available to validate client assertion.");
                return(fail);
            }

            var validAudiences = new[]
            {
                // token endpoint URL
                string.Concat((await _issuerNameService.GetCurrentAsync()).EnsureTrailingSlash(),
                              Constants.ProtocolRoutePaths.Token)
            };

            var tokenValidationParameters = new TokenValidationParameters
            {
                IssuerSigningKeys        = trustedKeys,
                ValidateIssuerSigningKey = true,

                ValidIssuer    = parsedSecret.Id,
                ValidateIssuer = true,

                ValidAudiences   = validAudiences,
                ValidateAudience = true,

                RequireSignedTokens   = true,
                RequireExpirationTime = true,

                ClockSkew = TimeSpan.FromMinutes(5)
            };

            try
            {
                var handler = new JwtSecurityTokenHandler();
                handler.ValidateToken(jwtTokenString, tokenValidationParameters, out var token);

                var jwtToken = (JwtSecurityToken)token;
                if (jwtToken.Subject != jwtToken.Issuer)
                {
                    _logger.LogError("Both 'sub' and 'iss' in the client assertion token must have a value of client_id.");
                    return(fail);
                }

                var exp = jwtToken.Payload.Exp;
                if (!exp.HasValue)
                {
                    _logger.LogError("exp is missing.");
                    return(fail);
                }

                var jti = jwtToken.Payload.Jti;
                if (jti.IsMissing())
                {
                    _logger.LogError("jti is missing.");
                    return(fail);
                }

                if (await _replayCache.ExistsAsync(Purpose, jti))
                {
                    _logger.LogError("jti is found in replay cache. Possible replay attack.");
                    return(fail);
                }
                else
                {
                    await _replayCache.AddAsync(Purpose, jti, DateTimeOffset.FromUnixTimeSeconds(exp.Value).AddMinutes(5));
                }

                return(success);
            }
            catch (Exception e)
            {
                _logger.LogError(e, "JWT token validation error");
                return(fail);
            }
        }