Browse Source

Expose the token validation parameters used by OpenIddict.Server and rework existing handlers

pull/818/head
Kévin Chalet 7 years ago
committed by GitHub
parent
commit
5627188737
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      src/OpenIddict.Server/OpenIddictServerBuilder.cs
  2. 2
      src/OpenIddict.Server/OpenIddictServerConfiguration.cs
  3. 130
      src/OpenIddict.Server/OpenIddictServerHandlers.cs
  4. 2
      src/OpenIddict.Server/OpenIddictServerJsonWebTokenHandler.cs
  5. 17
      src/OpenIddict.Server/OpenIddictServerOptions.cs
  6. 4
      src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionConfiguration.cs
  7. 8
      src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs
  8. 4
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpExtensions.cs
  9. 41
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlerFilters.cs
  10. 187
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs
  11. 12
      src/OpenIddict.Validation/OpenIddictValidationBuilder.cs
  12. 17
      src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs
  13. 7
      src/OpenIddict.Validation/OpenIddictValidationEvents.cs
  14. 102
      src/OpenIddict.Validation/OpenIddictValidationHandlers.cs
  15. 2
      src/OpenIddict.Validation/OpenIddictValidationJsonWebTokenHandler.cs
  16. 17
      src/OpenIddict.Validation/OpenIddictValidationOptions.cs

15
src/OpenIddict.Server/OpenIddictServerBuilder.cs

@ -1682,6 +1682,21 @@ namespace Microsoft.Extensions.DependencyInjection
return Configure(options => options.Issuer = address); return Configure(options => options.Issuer = address);
} }
/// <summary>
/// Updates the token validation parameters using the specified delegate.
/// </summary>
/// <param name="configuration">The configuration delegate.</param>
/// <returns>The <see cref="OpenIddictServerBuilder"/>.</returns>
public OpenIddictServerBuilder SetTokenValidationParameters([NotNull] Action<TokenValidationParameters> configuration)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
return Configure(options => configuration(options.TokenValidationParameters));
}
/// <summary> /// <summary>
/// Configures OpenIddict to use reference tokens, so that authorization codes, /// Configures OpenIddict to use reference tokens, so that authorization codes,
/// access tokens and refresh tokens are stored as ciphertext in the database /// access tokens and refresh tokens are stored as ciphertext in the database

2
src/OpenIddict.Server/OpenIddictServerConfiguration.cs

@ -35,7 +35,7 @@ namespace OpenIddict.Server
throw new ArgumentNullException(nameof(options)); throw new ArgumentNullException(nameof(options));
} }
if (options.SecurityTokenHandler == null) if (options.JsonWebTokenHandler == null)
{ {
throw new InvalidOperationException("The security token handler cannot be null."); throw new InvalidOperationException("The security token handler cannot be null.");
} }

130
src/OpenIddict.Server/OpenIddictServerHandlers.cs

@ -77,7 +77,9 @@ namespace OpenIddict.Server
AttachSelfContainedRefreshToken.Descriptor, AttachSelfContainedRefreshToken.Descriptor,
AttachTokenDigests.Descriptor, AttachTokenDigests.Descriptor,
AttachSelfContainedIdentityToken.Descriptor) AttachSelfContainedIdentityToken.Descriptor,
AttachAdditionalProperties.Descriptor)
.AddRange(Authentication.DefaultHandlers) .AddRange(Authentication.DefaultHandlers)
.AddRange(Discovery.DefaultHandlers) .AddRange(Discovery.DefaultHandlers)
@ -229,7 +231,7 @@ namespace OpenIddict.Server
} }
// If the token cannot be validated, don't return an error to allow another handle to validate it. // If the token cannot be validated, don't return an error to allow another handle to validate it.
if (!context.Options.SecurityTokenHandler.CanReadToken(payload)) if (!context.Options.JsonWebTokenHandler.CanReadToken(payload))
{ {
return; return;
} }
@ -267,15 +269,9 @@ namespace OpenIddict.Server
async ValueTask<TokenValidationResult> ValidateTokenAsync(string token, string type) async ValueTask<TokenValidationResult> ValidateTokenAsync(string token, string type)
{ {
var parameters = new TokenValidationParameters var parameters = context.Options.TokenValidationParameters.Clone();
{ parameters.PropertyBag = new Dictionary<string, object> { [Claims.Private.TokenUsage] = type };
NameClaimType = Claims.Name, parameters.ValidIssuer = context.Issuer?.AbsoluteUri;
PropertyBag = new Dictionary<string, object> { [Claims.Private.TokenUsage] = type },
RoleClaimType = Claims.Role,
ValidIssuer = context.Issuer?.AbsoluteUri,
ValidateAudience = false,
ValidateLifetime = false
};
parameters.IssuerSigningKeys = type switch parameters.IssuerSigningKeys = type switch
{ {
@ -298,7 +294,7 @@ namespace OpenIddict.Server
_ => Array.Empty<SecurityKey>() _ => Array.Empty<SecurityKey>()
}; };
return await context.Options.SecurityTokenHandler.ValidateTokenStringAsync(token, parameters); return await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(token, parameters);
} }
async ValueTask<TokenValidationResult> ValidateAnyTokenAsync(string token) async ValueTask<TokenValidationResult> ValidateAnyTokenAsync(string token)
@ -406,7 +402,7 @@ namespace OpenIddict.Server
}; };
// If the token cannot be validated, don't return an error to allow another handle to validate it. // If the token cannot be validated, don't return an error to allow another handle to validate it.
if (string.IsNullOrEmpty(token) || !context.Options.SecurityTokenHandler.CanReadToken(token)) if (string.IsNullOrEmpty(token) || !context.Options.JsonWebTokenHandler.CanReadToken(token))
{ {
return; return;
} }
@ -450,15 +446,9 @@ namespace OpenIddict.Server
async ValueTask<TokenValidationResult> ValidateTokenAsync(string token, string type) async ValueTask<TokenValidationResult> ValidateTokenAsync(string token, string type)
{ {
var parameters = new TokenValidationParameters var parameters = context.Options.TokenValidationParameters.Clone();
{ parameters.PropertyBag = new Dictionary<string, object> { [Claims.Private.TokenUsage] = type };
NameClaimType = Claims.Name, parameters.ValidIssuer = context.Issuer?.AbsoluteUri;
PropertyBag = new Dictionary<string, object> { [Claims.Private.TokenUsage] = type },
RoleClaimType = Claims.Role,
ValidIssuer = context.Issuer?.AbsoluteUri,
ValidateAudience = false,
ValidateLifetime = false
};
parameters.IssuerSigningKeys = type switch parameters.IssuerSigningKeys = type switch
{ {
@ -485,7 +475,7 @@ namespace OpenIddict.Server
_ => Array.Empty<SecurityKey>() _ => Array.Empty<SecurityKey>()
}; };
return await context.Options.SecurityTokenHandler.ValidateTokenStringAsync(token, parameters); return await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(token, parameters);
} }
async ValueTask<TokenValidationResult> ValidateAnyTokenAsync(string token) async ValueTask<TokenValidationResult> ValidateAnyTokenAsync(string token)
@ -1413,6 +1403,14 @@ namespace OpenIddict.Server
return true; return true;
} }
// Never include the public or internal token identifiers to ensure the identifiers
// that are automatically inherited from the parent token are not reused for the new token.
if (string.Equals(claim.Type, Claims.JwtId, StringComparison.OrdinalIgnoreCase) ||
string.Equals(claim.Type, Claims.Private.TokenId, StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Always exclude private claims, whose values must generally be kept secret. // Always exclude private claims, whose values must generally be kept secret.
if (claim.Type.StartsWith(Claims.Prefixes.Private, StringComparison.OrdinalIgnoreCase)) if (claim.Type.StartsWith(Claims.Prefixes.Private, StringComparison.OrdinalIgnoreCase))
{ {
@ -1437,11 +1435,8 @@ namespace OpenIddict.Server
claim.Properties.Remove(OpenIddictConstants.Properties.Destinations); claim.Properties.Remove(OpenIddictConstants.Properties.Destinations);
} }
// Note: the internal token identifier is automatically reset to ensure principal.SetClaim(Claims.JwtId, Guid.NewGuid().ToString());
// the identifier inherited from the parent token is not automatically reused. principal.SetCreationDate(DateTimeOffset.UtcNow);
principal.SetClaim(Claims.JwtId, Guid.NewGuid().ToString())
.SetCreationDate(DateTimeOffset.UtcNow)
.SetInternalTokenId(identifier: null);
var lifetime = context.Principal.GetAccessTokenLifetime() ?? context.Options.AccessTokenLifetime; var lifetime = context.Principal.GetAccessTokenLifetime() ?? context.Options.AccessTokenLifetime;
if (lifetime.HasValue) if (lifetime.HasValue)
@ -1512,12 +1507,24 @@ namespace OpenIddict.Server
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
// Note: the internal token identifier is automatically reset to ensure // Create a new principal containing only the filtered claims.
// the identifier inherited from the parent token is not automatically reused. // Actors identities are also filtered (delegation scenarios).
var principal = context.Principal.Clone(_ => true) var principal = context.Principal.Clone(claim =>
.SetClaim(Claims.JwtId, Guid.NewGuid().ToString()) {
.SetCreationDate(DateTimeOffset.UtcNow) // Never include the public or internal token identifiers to ensure the identifiers
.SetInternalTokenId(identifier: null); // that are automatically inherited from the parent token are not reused for the new token.
if (string.Equals(claim.Type, Claims.JwtId, StringComparison.OrdinalIgnoreCase) ||
string.Equals(claim.Type, Claims.Private.TokenId, StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Other claims are always included in the authorization code, even private claims.
return true;
});
principal.SetClaim(Claims.JwtId, Guid.NewGuid().ToString());
principal.SetCreationDate(DateTimeOffset.UtcNow);
var lifetime = context.Principal.GetAuthorizationCodeLifetime() ?? context.Options.AuthorizationCodeLifetime; var lifetime = context.Principal.GetAuthorizationCodeLifetime() ?? context.Options.AuthorizationCodeLifetime;
if (lifetime.HasValue) if (lifetime.HasValue)
@ -1587,12 +1594,24 @@ namespace OpenIddict.Server
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
// Note: the internal token identifier is automatically reset to ensure // Create a new principal containing only the filtered claims.
// the identifier inherited from the parent token is not automatically reused. // Actors identities are also filtered (delegation scenarios).
var principal = context.Principal.Clone(_ => true) var principal = context.Principal.Clone(claim =>
.SetClaim(Claims.JwtId, Guid.NewGuid().ToString()) {
.SetCreationDate(DateTimeOffset.UtcNow) // Never include the public or internal token identifiers to ensure the identifiers
.SetInternalTokenId(identifier: null); // that are automatically inherited from the parent token are not reused for the new token.
if (string.Equals(claim.Type, Claims.JwtId, StringComparison.OrdinalIgnoreCase) ||
string.Equals(claim.Type, Claims.Private.TokenId, StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Other claims are always included in the refresh token, even private claims.
return true;
});
principal.SetClaim(Claims.JwtId, Guid.NewGuid().ToString());
principal.SetCreationDate(DateTimeOffset.UtcNow);
// When sliding expiration is disabled, the expiration date of generated refresh tokens is fixed // When sliding expiration is disabled, the expiration date of generated refresh tokens is fixed
// and must exactly match the expiration date of the refresh token used in the token request. // and must exactly match the expiration date of the refresh token used in the token request.
@ -1663,6 +1682,14 @@ namespace OpenIddict.Server
return true; return true;
} }
// Never include the public or internal token identifiers to ensure the identifiers
// that are automatically inherited from the parent token are not reused for the new token.
if (string.Equals(claim.Type, Claims.JwtId, StringComparison.OrdinalIgnoreCase) ||
string.Equals(claim.Type, Claims.Private.TokenId, StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Always exclude private claims by default, whose values must generally be kept secret. // Always exclude private claims by default, whose values must generally be kept secret.
if (claim.Type.StartsWith(Claims.Prefixes.Private, StringComparison.OrdinalIgnoreCase)) if (claim.Type.StartsWith(Claims.Prefixes.Private, StringComparison.OrdinalIgnoreCase))
{ {
@ -1687,11 +1714,8 @@ namespace OpenIddict.Server
claim.Properties.Remove(OpenIddictConstants.Properties.Destinations); claim.Properties.Remove(OpenIddictConstants.Properties.Destinations);
} }
// Note: the internal token identifier is automatically reset to ensure principal.SetClaim(Claims.JwtId, Guid.NewGuid().ToString());
// the identifier inherited from the parent token is not automatically reused. principal.SetCreationDate(DateTimeOffset.UtcNow);
principal.SetClaim(Claims.JwtId, Guid.NewGuid().ToString())
.SetCreationDate(DateTimeOffset.UtcNow)
.SetInternalTokenId(identifier: null);
var lifetime = context.Principal.GetIdentityTokenLifetime() ?? context.Options.IdentityTokenLifetime; var lifetime = context.Principal.GetIdentityTokenLifetime() ?? context.Options.IdentityTokenLifetime;
if (lifetime.HasValue) if (lifetime.HasValue)
@ -2058,7 +2082,7 @@ namespace OpenIddict.Server
descriptor.ApplicationId = await _applicationManager.GetIdAsync(application); descriptor.ApplicationId = await _applicationManager.GetIdAsync(application);
} }
descriptor.Payload = await context.Options.SecurityTokenHandler.CreateTokenFromDescriptorAsync( descriptor.Payload = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor new SecurityTokenDescriptor
{ {
Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AccessToken }, Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AccessToken },
@ -2169,7 +2193,7 @@ namespace OpenIddict.Server
descriptor.ApplicationId = await _applicationManager.GetIdAsync(application); descriptor.ApplicationId = await _applicationManager.GetIdAsync(application);
} }
descriptor.Payload = await context.Options.SecurityTokenHandler.CreateTokenFromDescriptorAsync( descriptor.Payload = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor new SecurityTokenDescriptor
{ {
Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AuthorizationCode }, Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AuthorizationCode },
@ -2280,7 +2304,7 @@ namespace OpenIddict.Server
descriptor.ApplicationId = await _applicationManager.GetIdAsync(application); descriptor.ApplicationId = await _applicationManager.GetIdAsync(application);
} }
descriptor.Payload = await context.Options.SecurityTokenHandler.CreateTokenFromDescriptorAsync( descriptor.Payload = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor new SecurityTokenDescriptor
{ {
Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.RefreshToken }, Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.RefreshToken },
@ -2510,7 +2534,7 @@ namespace OpenIddict.Server
return; return;
} }
context.Response.AccessToken = await context.Options.SecurityTokenHandler.CreateTokenFromDescriptorAsync( context.Response.AccessToken = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor new SecurityTokenDescriptor
{ {
Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AccessToken }, Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AccessToken },
@ -2561,7 +2585,7 @@ namespace OpenIddict.Server
return; return;
} }
context.Response.Code = await context.Options.SecurityTokenHandler.CreateTokenFromDescriptorAsync( context.Response.Code = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor new SecurityTokenDescriptor
{ {
Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AuthorizationCode }, Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AuthorizationCode },
@ -2612,7 +2636,7 @@ namespace OpenIddict.Server
return; return;
} }
context.Response.RefreshToken = await context.Options.SecurityTokenHandler.CreateTokenFromDescriptorAsync( context.Response.RefreshToken = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor new SecurityTokenDescriptor
{ {
Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.RefreshToken }, Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.RefreshToken },
@ -2793,7 +2817,7 @@ namespace OpenIddict.Server
return; return;
} }
context.Response.IdToken = await context.Options.SecurityTokenHandler.CreateTokenFromDescriptorAsync( context.Response.IdToken = await context.Options.JsonWebTokenHandler.CreateTokenFromDescriptorAsync(
new SecurityTokenDescriptor new SecurityTokenDescriptor
{ {
Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.IdToken }, Claims = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.IdToken },
@ -2850,7 +2874,7 @@ namespace OpenIddict.Server
// If the granted access token scopes differ from the requested scopes, return the granted scopes // If the granted access token scopes differ from the requested scopes, return the granted scopes
// list as a parameter to inform the client application of the fact the scopes set will be reduced. // list as a parameter to inform the client application of the fact the scopes set will be reduced.
if (context.Request.IsAuthorizationCodeGrantType() || if ((context.EndpointType == OpenIddictServerEndpointType.Token && context.Request.IsAuthorizationCodeGrantType()) ||
!context.AccessTokenPrincipal.GetScopes().SetEquals(context.Request.GetScopes())) !context.AccessTokenPrincipal.GetScopes().SetEquals(context.Request.GetScopes()))
{ {
context.Response.Scope = string.Join(" ", context.AccessTokenPrincipal.GetScopes()); context.Response.Scope = string.Join(" ", context.AccessTokenPrincipal.GetScopes());

2
src/OpenIddict.Server/OpenIddictServerTokenHandler.cs → src/OpenIddict.Server/OpenIddictServerJsonWebTokenHandler.cs

@ -15,7 +15,7 @@ using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Server namespace OpenIddict.Server
{ {
public class OpenIddictServerTokenHandler : JsonWebTokenHandler public class OpenIddictServerJsonWebTokenHandler : JsonWebTokenHandler
{ {
public ValueTask<string> CreateTokenFromDescriptorAsync(SecurityTokenDescriptor descriptor) public ValueTask<string> CreateTokenFromDescriptorAsync(SecurityTokenDescriptor descriptor)
{ {

17
src/OpenIddict.Server/OpenIddictServerOptions.cs

@ -84,13 +84,26 @@ namespace OpenIddict.Server
public IList<Uri> UserinfoEndpointUris { get; } = new List<Uri>(); public IList<Uri> UserinfoEndpointUris { get; } = new List<Uri>();
/// <summary> /// <summary>
/// Gets or sets the security token handler used to protect and unprotect tokens. /// Gets or sets the JWT handler used to protect and unprotect tokens.
/// </summary> /// </summary>
public OpenIddictServerTokenHandler SecurityTokenHandler { get; set; } = new OpenIddictServerTokenHandler public OpenIddictServerJsonWebTokenHandler JsonWebTokenHandler { get; set; } = new OpenIddictServerJsonWebTokenHandler
{ {
SetDefaultTimesOnTokenCreation = false SetDefaultTimesOnTokenCreation = false
}; };
/// <summary>
/// Gets the token validation parameters used by the OpenIddict server services.
/// </summary>
public TokenValidationParameters TokenValidationParameters { get; } = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero,
NameClaimType = OpenIddictConstants.Claims.Name,
RoleClaimType = OpenIddictConstants.Claims.Role,
// Note: audience and lifetime are manually validated by OpenIddict itself.
ValidateAudience = false,
ValidateLifetime = false
};
/// <summary> /// <summary>
/// Gets or sets the period of time the authorization codes remain valid after being issued. /// Gets or sets the period of time the authorization codes remain valid after being issued.
/// While not recommended, this property can be set to <c>null</c> to issue codes that never expire. /// While not recommended, this property can be set to <c>null</c> to issue codes that never expire.

4
src/OpenIddict.Validation.DataProtection/OpenIddictValidationDataProtectionConfiguration.cs

@ -34,10 +34,6 @@ namespace OpenIddict.Validation.DataProtection
throw new ArgumentNullException(nameof(options)); throw new ArgumentNullException(nameof(options));
} }
// Use empty token validation parameters to ensure the core OpenIddict validation components
// don't throw an exception stating that an issuer or a metadata address was not set.
options.TokenValidationParameters = new TokenValidationParameters();
// Register the built-in event handlers used by the OpenIddict Data Protection validation components. // Register the built-in event handlers used by the OpenIddict Data Protection validation components.
foreach (var handler in OpenIddictValidationDataProtectionHandlers.DefaultHandlers) foreach (var handler in OpenIddictValidationDataProtectionHandlers.DefaultHandlers)
{ {

8
src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs

@ -44,11 +44,9 @@ namespace OpenIddict.Validation.ServerIntegration
options.Issuer = _options.CurrentValue.Issuer; options.Issuer = _options.CurrentValue.Issuer;
// Import the token validation parameters from the server configuration. // Import the token validation parameters from the server configuration.
options.TokenValidationParameters = new TokenValidationParameters options.TokenValidationParameters.IssuerSigningKeys =
{ (from credentials in _options.CurrentValue.SigningCredentials
IssuerSigningKeys = (from credentials in _options.CurrentValue.SigningCredentials select credentials.Key).ToList();
select credentials.Key).ToList()
};
// Import the symmetric encryption keys from the server configuration. // Import the symmetric encryption keys from the server configuration.
foreach (var credentials in _options.CurrentValue.EncryptionCredentials) foreach (var credentials in _options.CurrentValue.EncryptionCredentials)

4
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpExtensions.cs

@ -12,6 +12,7 @@ using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using OpenIddict.Validation; using OpenIddict.Validation;
using OpenIddict.Validation.SystemNetHttp; using OpenIddict.Validation.SystemNetHttp;
using static OpenIddict.Validation.SystemNetHttp.OpenIddictValidationSystemNetHttpHandlerFilters;
using static OpenIddict.Validation.SystemNetHttp.OpenIddictValidationSystemNetHttpHandlers; using static OpenIddict.Validation.SystemNetHttp.OpenIddictValidationSystemNetHttpHandlers;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
@ -41,6 +42,9 @@ namespace Microsoft.Extensions.DependencyInjection
// Note: the order used here is not important, as the actual order is set in the options. // Note: the order used here is not important, as the actual order is set in the options.
builder.Services.TryAdd(DefaultHandlers.Select(descriptor => descriptor.ServiceDescriptor)); builder.Services.TryAdd(DefaultHandlers.Select(descriptor => descriptor.ServiceDescriptor));
// Register the built-in filters used by the default OpenIddict System.Net.Http event handlers.
builder.Services.TryAddSingleton<RequireHttpMetadataAddress>();
// Note: TryAddEnumerable() is used here to ensure the initializers are registered only once. // Note: TryAddEnumerable() is used here to ensure the initializers are registered only once.
builder.Services.TryAddEnumerable(new[] builder.Services.TryAddEnumerable(new[]
{ {

41
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlerFilters.cs

@ -0,0 +1,41 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using JetBrains.Annotations;
using static OpenIddict.Validation.OpenIddictValidationEvents;
namespace OpenIddict.Validation.SystemNetHttp
{
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static class OpenIddictValidationSystemNetHttpHandlerFilters
{
/// <summary>
/// Represents a filter that excludes the associated handlers if the metadata address of the issuer is not available.
/// </summary>
public class RequireHttpMetadataAddress : IOpenIddictValidationHandlerFilter<BaseContext>
{
public ValueTask<bool> IsActiveAsync([NotNull] BaseContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Options.MetadataAddress == null)
{
return new ValueTask<bool>(false);
}
return new ValueTask<bool>(
string.Equals(context.Options.MetadataAddress.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) ||
string.Equals(context.Options.MetadataAddress.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase));
}
}
}
}

187
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpHandlers.cs

@ -23,6 +23,7 @@ using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Validation.OpenIddictValidationEvents; using static OpenIddict.Validation.OpenIddictValidationEvents;
using static OpenIddict.Validation.OpenIddictValidationHandlers; using static OpenIddict.Validation.OpenIddictValidationHandlers;
using static OpenIddict.Validation.SystemNetHttp.OpenIddictValidationSystemNetHttpConstants; using static OpenIddict.Validation.SystemNetHttp.OpenIddictValidationSystemNetHttpConstants;
using static OpenIddict.Validation.SystemNetHttp.OpenIddictValidationSystemNetHttpHandlerFilters;
namespace OpenIddict.Validation.SystemNetHttp namespace OpenIddict.Validation.SystemNetHttp
{ {
@ -33,79 +34,33 @@ namespace OpenIddict.Validation.SystemNetHttp
/* /*
* Authentication processing: * Authentication processing:
*/ */
PopulateTokenValidationParametersFromMemoryCache.Descriptor, PopulateTokenValidationParameters.Descriptor);
PopulateTokenValidationParametersFromProviderConfiguration.Descriptor,
CacheTokenValidationParameters.Descriptor);
/// <summary>
/// Contains the logic responsible of populating the token validation parameters from the memory cache.
/// </summary>
public class PopulateTokenValidationParametersFromMemoryCache : IOpenIddictValidationHandler<ProcessAuthenticationContext>
{
private readonly IMemoryCache _cache;
public PopulateTokenValidationParametersFromMemoryCache([NotNull] IMemoryCache cache)
=> _cache = cache;
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.UseSingletonHandler<PopulateTokenValidationParametersFromMemoryCache>()
.SetOrder(PopulateTokenValidationParametersFromProviderConfiguration.Descriptor.Order - 1_000)
.Build();
public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// If token validation parameters were already attached, don't overwrite them.
if (context.TokenValidationParameters != null)
{
return default;
}
// If the metadata address is not an HTTP/HTTPS address, let another handler populate the validation parameters.
if (!string.Equals(context.Options.MetadataAddress.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(context.Options.MetadataAddress.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
{
return default;
}
// Resolve the token validation parameters from the memory cache.
if (_cache.TryGetValue(
key: string.Concat("af84c073-c27c-49fd-a54f-584fd60320d3", "\x1e", context.Issuer?.AbsoluteUri),
value: out TokenValidationParameters parameters))
{
context.TokenValidationParameters = parameters;
}
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible of populating the token validation /// Contains the logic responsible of populating the token validation
/// parameters using OAuth 2.0/OpenID Connect discovery. /// parameters using OAuth 2.0/OpenID Connect discovery.
/// </summary> /// </summary>
public class PopulateTokenValidationParametersFromProviderConfiguration : IOpenIddictValidationHandler<ProcessAuthenticationContext> public class PopulateTokenValidationParameters : IOpenIddictValidationHandler<ProcessAuthenticationContext>
{ {
private readonly IMemoryCache _cache;
private readonly IHttpClientFactory _factory; private readonly IHttpClientFactory _factory;
public PopulateTokenValidationParametersFromProviderConfiguration([NotNull] IHttpClientFactory factory) public PopulateTokenValidationParameters(
=> _factory = factory; [NotNull] IMemoryCache cache,
[NotNull] IHttpClientFactory factory)
{
_cache = cache;
_factory = factory;
}
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
/// </summary> /// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.UseSingletonHandler<PopulateTokenValidationParametersFromProviderConfiguration>() .AddFilter<RequireHttpMetadataAddress>()
.SetOrder(ValidateTokenValidationParameters.Descriptor.Order - 1_000) .UseSingletonHandler<PopulateTokenValidationParameters>()
.SetOrder(ValidateSelfContainedToken.Descriptor.Order - 500)
.Build(); .Build();
public async ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context) public async ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
@ -115,42 +70,45 @@ namespace OpenIddict.Validation.SystemNetHttp
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
// If token validation parameters were already attached, don't overwrite them. var parameters = await _cache.GetOrCreateAsync(
if (context.TokenValidationParameters != null) key: string.Concat("af84c073-c27c-49fd-a54f-584fd60320d3", "\x1e", context.Issuer?.AbsoluteUri),
{ factory: async entry =>
return; {
} entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(30));
entry.SetPriority(CacheItemPriority.NeverRemove);
// If the metadata address is not an HTTP/HTTPS address, let another handler populate the validation parameters. return await GetTokenValidationParametersAsync();
if (!string.Equals(context.Options.MetadataAddress.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && });
!string.Equals(context.Options.MetadataAddress.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
{
return;
}
using var client = _factory.CreateClient(Clients.Discovery); context.TokenValidationParameters.ValidIssuer = parameters.ValidIssuer;
var response = await SendHttpRequestMessageAsync(context.Options.MetadataAddress); context.TokenValidationParameters.IssuerSigningKeys = parameters.IssuerSigningKeys;
// Ensure the JWKS endpoint URL is present and valid. async ValueTask<TokenValidationParameters> GetTokenValidationParametersAsync()
if (!response.TryGetParameter(Metadata.JwksUri, out var endpoint) || OpenIddictParameter.IsNullOrEmpty(endpoint))
{ {
throw new InvalidOperationException("A discovery response containing an empty JWKS endpoint URL was returned."); using var client = _factory.CreateClient(Clients.Discovery);
} var response = await SendHttpRequestMessageAsync(client, context.Options.MetadataAddress);
if (!Uri.TryCreate((string) endpoint, UriKind.Absolute, out Uri uri)) // Ensure the JWKS endpoint URL is present and valid.
{ if (!response.TryGetParameter(Metadata.JwksUri, out var endpoint) || OpenIddictParameter.IsNullOrEmpty(endpoint))
throw new InvalidOperationException("A discovery response containing an invalid JWKS endpoint URL was returned."); {
} throw new InvalidOperationException("A discovery response containing an empty JWKS endpoint URL was returned.");
}
context.TokenValidationParameters = new TokenValidationParameters if (!Uri.TryCreate((string) endpoint, UriKind.Absolute, out Uri uri))
{ {
ValidIssuer = (string) response[Metadata.Issuer], throw new InvalidOperationException("A discovery response containing an invalid JWKS endpoint URL was returned.");
IssuerSigningKeys = await GetSigningKeysAsync(uri).ToListAsync() }
};
return new TokenValidationParameters
{
ValidIssuer = (string) response[Metadata.Issuer],
IssuerSigningKeys = await GetSigningKeysAsync(client, uri).ToListAsync()
};
}
async IAsyncEnumerable<SecurityKey> GetSigningKeysAsync(Uri address) async IAsyncEnumerable<SecurityKey> GetSigningKeysAsync(HttpClient client, Uri address)
{ {
var response = await SendHttpRequestMessageAsync(address); var response = await SendHttpRequestMessageAsync(client, address);
var keys = response[JsonWebKeySetParameterNames.Keys]; var keys = response[JsonWebKeySetParameterNames.Keys];
if (keys == null) if (keys == null)
@ -208,7 +166,7 @@ namespace OpenIddict.Validation.SystemNetHttp
} }
} }
async ValueTask<OpenIddictResponse> SendHttpRequestMessageAsync(Uri address) static async ValueTask<OpenIddictResponse> SendHttpRequestMessageAsync(HttpClient client, Uri address)
{ {
using var request = new HttpRequestMessage(HttpMethod.Get, address); using var request = new HttpRequestMessage(HttpMethod.Get, address);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
@ -243,58 +201,5 @@ namespace OpenIddict.Validation.SystemNetHttp
} }
} }
} }
/// <summary>
/// Contains the logic responsible of caching the token validation parameters.
/// </summary>
public class CacheTokenValidationParameters : IOpenIddictValidationHandler<ProcessAuthenticationContext>
{
private readonly IMemoryCache _cache;
public CacheTokenValidationParameters([NotNull] IMemoryCache cache)
=> _cache = cache;
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.UseSingletonHandler<CacheTokenValidationParameters>()
.SetOrder(ValidateTokenValidationParameters.Descriptor.Order + 500)
.Build();
public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.TokenValidationParameters == null)
{
return default;
}
// If the metadata address is not an HTTP/HTTPS address, let another handler populate the validation parameters.
if (!string.Equals(context.Options.MetadataAddress.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(context.Options.MetadataAddress.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
{
return default;
}
// Store the token validation parameters in the memory cache.
_ = _cache.GetOrCreate(
key: string.Concat("af84c073-c27c-49fd-a54f-584fd60320d3", "\x1e", context.Issuer?.AbsoluteUri),
factory: entry =>
{
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(30));
entry.SetPriority(CacheItemPriority.NeverRemove);
return context.TokenValidationParameters;
});
return default;
}
}
} }
} }

12
src/OpenIddict.Validation/OpenIddictValidationBuilder.cs

@ -605,18 +605,18 @@ namespace Microsoft.Extensions.DependencyInjection
} }
/// <summary> /// <summary>
/// Sets the static token validation parameters. /// Updates the token validation parameters using the specified delegate.
/// </summary> /// </summary>
/// <param name="parameters">The issuer address.</param> /// <param name="configuration">The configuration delegate.</param>
/// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns> /// <returns>The <see cref="OpenIddictValidationBuilder"/>.</returns>
public OpenIddictValidationBuilder SetTokenValidationParameters([NotNull] TokenValidationParameters parameters) public OpenIddictValidationBuilder SetTokenValidationParameters([NotNull] Action<TokenValidationParameters> configuration)
{ {
if (parameters == null) if (configuration == null)
{ {
throw new ArgumentNullException(nameof(parameters)); throw new ArgumentNullException(nameof(configuration));
} }
return Configure(options => options.TokenValidationParameters = parameters); return Configure(options => configuration(options.TokenValidationParameters));
} }
/// <summary> /// <summary>

17
src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs

@ -7,7 +7,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
@ -32,25 +31,13 @@ namespace OpenIddict.Validation
throw new ArgumentNullException(nameof(options)); throw new ArgumentNullException(nameof(options));
} }
if (options.SecurityTokenHandler == null) if (options.JsonWebTokenHandler == null)
{ {
throw new InvalidOperationException("The security token handler cannot be null."); throw new InvalidOperationException("The security token handler cannot be null.");
} }
if (options.TokenValidationParameters == null) if (options.Issuer != null || options.MetadataAddress != null)
{ {
if (options.Issuer == null && options.MetadataAddress == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The authority or an absolute metadata endpoint address must be provided.")
.Append("Alternatively, token validation parameters can be manually set by calling ")
.AppendLine("'services.AddOpenIddict().AddValidation().SetTokenValidationParameters()'.")
.Append("To use the server configuration of a local OpenIddict server instance, ")
.Append("reference the 'OpenIddict.Validation.ServerIntegration' package ")
.Append("and call 'services.AddOpenIddict().AddValidation().UseLocalServer()'.")
.ToString());
}
if (options.MetadataAddress == null) if (options.MetadataAddress == null)
{ {
options.MetadataAddress = new Uri(".well-known/openid-configuration", UriKind.Relative); options.MetadataAddress = new Uri(".well-known/openid-configuration", UriKind.Relative);

7
src/OpenIddict.Validation/OpenIddictValidationEvents.cs

@ -240,13 +240,12 @@ namespace OpenIddict.Validation
/// </summary> /// </summary>
public ProcessAuthenticationContext([NotNull] OpenIddictValidationTransaction transaction) public ProcessAuthenticationContext([NotNull] OpenIddictValidationTransaction transaction)
: base(transaction) : base(transaction)
{ => TokenValidationParameters = transaction.Options.TokenValidationParameters.Clone();
}
/// <summary> /// <summary>
/// Gets or sets the token validation parameters used for the current request. /// Gets the token validation parameters used for the current request.
/// </summary> /// </summary>
public TokenValidationParameters TokenValidationParameters { get; set; } public TokenValidationParameters TokenValidationParameters { get; }
/// <summary> /// <summary>
/// Gets or sets the security principal. /// Gets or sets the security principal.

102
src/OpenIddict.Validation/OpenIddictValidationHandlers.cs

@ -28,7 +28,6 @@ namespace OpenIddict.Validation
/* /*
* Authentication processing: * Authentication processing:
*/ */
ValidateTokenValidationParameters.Descriptor,
ValidateAccessTokenParameter.Descriptor, ValidateAccessTokenParameter.Descriptor,
ValidateReferenceToken.Descriptor, ValidateReferenceToken.Descriptor,
ValidateSelfContainedToken.Descriptor, ValidateSelfContainedToken.Descriptor,
@ -42,64 +41,6 @@ namespace OpenIddict.Validation
*/ */
AttachDefaultChallengeError.Descriptor); AttachDefaultChallengeError.Descriptor);
/// <summary>
/// Contains the logic responsible of ensuring the token validation parameters are populated.
/// </summary>
public class ValidateTokenValidationParameters : IOpenIddictValidationHandler<ProcessAuthenticationContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.UseSingletonHandler<ValidateTokenValidationParameters>()
.SetOrder(int.MinValue + 100_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// Note: at this stage, throw an exception if the token validation parameters cannot be found.
var parameters = context.TokenValidationParameters ?? context.Options.TokenValidationParameters;
if (parameters == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The token validation parameters cannot be retrieved.")
.Append("To register the default client, reference the 'OpenIddict.Validation.SystemNetHttp' package ")
.AppendLine("and call 'services.AddOpenIddict().AddValidation().UseSystemNetHttp()'.")
.Append("Alternatively, you can manually provide the token validation parameters ")
.Append("by calling 'services.AddOpenIddict().AddValidation().SetTokenValidationParameters()'.")
.ToString());
}
// Clone the token validation parameters before mutating them to ensure the
// shared token validation parameters registered as options are not modified.
parameters = parameters.Clone();
parameters.NameClaimType = Claims.Name;
parameters.PropertyBag = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AccessToken };
parameters.RoleClaimType = Claims.Role;
parameters.TokenDecryptionKeys = context.Options.EncryptionCredentials.Select(credentials => credentials.Key);
parameters.ValidIssuer = context.Issuer?.AbsoluteUri;
parameters.ValidateAudience = false;
parameters.ValidateLifetime = false;
context.TokenValidationParameters = parameters;
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible of validating the access token resolved from the current request. /// Contains the logic responsible of validating the access token resolved from the current request.
/// </summary> /// </summary>
@ -111,7 +52,7 @@ namespace OpenIddict.Validation
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.UseSingletonHandler<ValidateAccessTokenParameter>() .UseSingletonHandler<ValidateAccessTokenParameter>()
.SetOrder(ValidateTokenValidationParameters.Descriptor.Order + 1_000) .SetOrder(int.MinValue + 100_000)
.Build(); .Build();
/// <summary> /// <summary>
@ -205,15 +146,27 @@ namespace OpenIddict.Validation
} }
// If the token cannot be validated, don't return an error to allow another handle to validate it. // If the token cannot be validated, don't return an error to allow another handle to validate it.
if (!context.Options.SecurityTokenHandler.CanReadToken(payload)) if (!context.Options.JsonWebTokenHandler.CanReadToken(payload))
{ {
return; return;
} }
// If the token cannot be validated, don't return an error to allow another handle to validate it. // If no issuer signing key was attached, don't return an error to allow another handle to validate it.
var result = await context.Options.SecurityTokenHandler.ValidateTokenStringAsync( var parameters = context.TokenValidationParameters;
payload, context.TokenValidationParameters); if (parameters?.IssuerSigningKeys == null)
{
return;
}
// Clone the token validation parameters before mutating them to ensure the
// shared token validation parameters registered as options are not modified.
parameters = parameters.Clone();
parameters.PropertyBag = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AccessToken };
parameters.TokenDecryptionKeys = context.Options.EncryptionCredentials.Select(credentials => credentials.Key);
parameters.ValidIssuer = context.Issuer?.AbsoluteUri;
// If the token cannot be validated, don't return an error to allow another handle to validate it.
var result = await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(payload, parameters);
if (result.ClaimsIdentity == null) if (result.ClaimsIdentity == null)
{ {
return; return;
@ -265,15 +218,26 @@ namespace OpenIddict.Validation
} }
// If the token cannot be validated, don't return an error to allow another handle to validate it. // If the token cannot be validated, don't return an error to allow another handle to validate it.
if (!context.Options.SecurityTokenHandler.CanReadToken(context.Request.AccessToken)) if (!context.Options.JsonWebTokenHandler.CanReadToken(context.Request.AccessToken))
{ {
return; return;
} }
// If the token cannot be validated, don't return an error to allow another handle to validate it. // If no issuer signing key was attached, don't return an error to allow another handle to validate it.
var result = await context.Options.SecurityTokenHandler.ValidateTokenStringAsync( var parameters = context.TokenValidationParameters;
context.Request.AccessToken, context.TokenValidationParameters); if (parameters?.IssuerSigningKeys == null)
{
return;
}
// Clone the token validation parameters before mutating them.
parameters = parameters.Clone();
parameters.PropertyBag = new Dictionary<string, object> { [Claims.Private.TokenUsage] = TokenUsages.AccessToken };
parameters.TokenDecryptionKeys = context.Options.EncryptionCredentials.Select(credentials => credentials.Key);
parameters.ValidIssuer = context.Issuer?.AbsoluteUri;
// If the token cannot be validated, don't return an error to allow another handle to validate it.
var result = await context.Options.JsonWebTokenHandler.ValidateTokenStringAsync(context.Request.AccessToken, parameters);
if (result.ClaimsIdentity == null) if (result.ClaimsIdentity == null)
{ {
return; return;
@ -295,7 +259,7 @@ namespace OpenIddict.Validation
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.UseSingletonHandler<ValidatePrincipal>() .UseSingletonHandler<ValidatePrincipal>()
.SetOrder(ValidateSelfContainedToken.Descriptor.Order + 1_000) .SetOrder(ValidateReferenceToken.Descriptor.Order + 1_000)
.Build(); .Build();
/// <summary> /// <summary>

2
src/OpenIddict.Validation/OpenIddictValidationTokenHandler.cs → src/OpenIddict.Validation/OpenIddictValidationJsonWebTokenHandler.cs

@ -15,7 +15,7 @@ using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Validation namespace OpenIddict.Validation
{ {
public class OpenIddictValidationTokenHandler : JsonWebTokenHandler public class OpenIddictValidationJsonWebTokenHandler : JsonWebTokenHandler
{ {
public ValueTask<TokenValidationResult> ValidateTokenStringAsync(string token, TokenValidationParameters parameters) public ValueTask<TokenValidationResult> ValidateTokenStringAsync(string token, TokenValidationParameters parameters)
{ {

17
src/OpenIddict.Validation/OpenIddictValidationOptions.cs

@ -7,6 +7,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using OpenIddict.Abstractions;
namespace OpenIddict.Validation namespace OpenIddict.Validation
{ {
@ -22,9 +23,9 @@ namespace OpenIddict.Validation
public IList<EncryptingCredentials> EncryptionCredentials { get; } = new List<EncryptingCredentials>(); public IList<EncryptingCredentials> EncryptionCredentials { get; } = new List<EncryptingCredentials>();
/// <summary> /// <summary>
/// Gets or sets the security token handler used to protect and unprotect tokens. /// Gets or sets the JWT handler used to protect and unprotect tokens.
/// </summary> /// </summary>
public OpenIddictValidationTokenHandler SecurityTokenHandler { get; set; } = new OpenIddictValidationTokenHandler public OpenIddictValidationJsonWebTokenHandler JsonWebTokenHandler { get; set; } = new OpenIddictValidationJsonWebTokenHandler
{ {
SetDefaultTimesOnTokenCreation = false SetDefaultTimesOnTokenCreation = false
}; };
@ -79,8 +80,16 @@ namespace OpenIddict.Validation
public ISet<string> Audiences { get; } = new HashSet<string>(StringComparer.Ordinal); public ISet<string> Audiences { get; } = new HashSet<string>(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets or sets the token validation parameters used by the OpenIddict validation services. /// Gets the token validation parameters used by the OpenIddict validation services.
/// </summary> /// </summary>
public TokenValidationParameters TokenValidationParameters { get; set; } public TokenValidationParameters TokenValidationParameters { get; } = new TokenValidationParameters
{
ClockSkew = TimeSpan.Zero,
NameClaimType = OpenIddictConstants.Claims.Name,
RoleClaimType = OpenIddictConstants.Claims.Role,
// Note: audience and lifetime are manually validated by OpenIddict itself.
ValidateAudience = false,
ValidateLifetime = false
};
} }
} }

Loading…
Cancel
Save