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