diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx
index db1dd8f7..82e4ffbe 100644
--- a/src/OpenIddict.Abstractions/OpenIddictResources.resx
+++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx
@@ -117,13 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- An identity cannot be extracted from this token request.
-This generally indicates that the OpenIddict server stack was asked to validate a token for an invalid grant type (e.g password).
-
An identity cannot be extracted from this request.
-This generally indicates that the OpenIddict server stack was asked to validate a token for an endpoint it doesn't manage.
+This generally indicates that the OpenIddict server stack was asked to authenticate a request for an endpoint it doesn't manage.
To validate tokens received by custom API endpoints, the OpenIddict validation handler (e.g OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme or OpenIddictValidationOwinDefaults.AuthenticationType) must be used instead.
@@ -426,19 +422,19 @@ To use key rollover, register both the new certificate and the old one in the cr
No custom authorization request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateAuthorizationRequestContext>' must be implemented to validate authorization requests (e.g to ensure the client_id and redirect_uri are valid).
- No custom device request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateDeviceRequestContext>' must be implemented to validate device requests (e.g to ensure the client_id and client_secret are valid).
+ No custom device request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateDeviceRequestContext>' (or 'IOpenIddictServerHandler<ProcessAuthenticationContext>') must be implemented to validate device requests (e.g to ensure the client_id and client_secret are valid).
- No custom introspection request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateIntrospectionRequestContext>' must be implemented to validate introspection requests (e.g to ensure the client_id and client_secret are valid).
+ No custom introspection request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateIntrospectionRequestContext>' (or 'IOpenIddictServerHandler<ProcessAuthenticationContext>') must be implemented to validate introspection requests (e.g to ensure the client_id and client_secret are valid).
No custom logout request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateLogoutRequestContext>' must be implemented to validate logout requests (e.g to ensure the post_logout_redirect_uri is valid).
- No custom revocation request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateRevocationRequestContext>' must be implemented to validate revocation requests (e.g to ensure the client_id and client_secret are valid).
+ No custom revocation request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateRevocationRequestContext>' (or 'IOpenIddictServerHandler<ProcessAuthenticationContext>') must be implemented to validate revocation requests (e.g to ensure the client_id and client_secret are valid).
- No custom token request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateTokenRequestContext>' must be implemented to validate token requests (e.g to ensure the client_id and client_secret are valid).
+ No custom token request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateTokenRequestContext>' (or 'IOpenIddictServerHandler<ProcessAuthenticationContext>') must be implemented to validate token requests (e.g to ensure the client_id and client_secret are valid).
No custom verification request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler<ValidateVerificationRequestContext>' must be implemented to validate verification requests (e.g to ensure the user_code is valid).
@@ -2235,9 +2231,6 @@ The principal used to create the token contained the following claims: {Claims}.
The authorization request was rejected because the specified response type was not compatible with PKCE.
-
- The authorization request was rejected because the client application was not found: '{ClientId}'.
-
The authorization request was rejected because the confidential application '{ClientId}' was not allowed to retrieve an access token from the authorization endpoint.
@@ -2271,24 +2264,9 @@ The principal used to create the token contained the following claims: {Claims}.
The device request was successfully validated.
-
- The device request was rejected because the mandatory '{Parameter}' parameter was missing.
-
The device request was rejected because invalid scopes were specified: {Scopes}.
-
- The device request was rejected because the client application was not found: '{ClientId}'.
-
-
- The device request was rejected because the public application '{ClientId}' was not allowed to send a client secret.
-
-
- The device request was rejected because the confidential application '{ClientId}' didn't specify a client secret.
-
-
- The device request was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.
-
The device request was rejected because the application '{ClientId}' was not allowed to use the device endpoint.
@@ -2346,21 +2324,6 @@ The principal used to create the token contained the following claims: {Claims}.
The token request was rejected because invalid scopes were specified: {Scopes}.
-
- The token request was rejected because the client application was not found: '{ClientId}'.
-
-
- The token request was rejected because the public client application '{ClientId}' was not allowed to use the client credentials grant.
-
-
- The token request was rejected because the public application '{ClientId}' was not allowed to send a client secret.
-
-
- The token request was rejected because the confidential application '{ClientId}' didn't specify a client secret.
-
-
- The token request was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.
-
The token request was rejected because the application '{ClientId}' was not allowed to use the token endpoint.
@@ -2400,18 +2363,6 @@ The principal used to create the token contained the following claims: {Claims}.
The introspection request was rejected because the mandatory '{Parameter}' parameter was missing.
-
- The introspection request was rejected because the client application was not found: '{ClientId}'.
-
-
- The introspection request was rejected because the public application '{ClientId}' was not allowed to send a client secret.
-
-
- The introspection request was rejected because the confidential application '{ClientId}' didn't specify a client secret.
-
-
- The introspection request was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.
-
The introspection request was rejected because the application '{ClientId}' was not allowed to use the introspection endpoint.
@@ -2439,18 +2390,6 @@ The principal used to create the token contained the following claims: {Claims}.
The revocation request was rejected because the mandatory '{Parameter}' parameter was missing.
-
- The revocation request was rejected because the client application was not found: '{ClientId}'.
-
-
- The revocation request was rejected because the public application '{ClientId}' was not allowed to send a client secret.
-
-
- The revocation request was rejected because the confidential application '{ClientId}' didn't specify a client secret.
-
-
- The revocation request was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.
-
The revocation request was rejected because the application '{ClientId}' was not allowed to use the revocation endpoint.
@@ -2689,9 +2628,6 @@ This may indicate that the hashed entry is corrupted or malformed.
The userinfo response returned by {Uri} was successfully extracted: {Response}.
-
- The logout request was rejected because the client application was not found: '{ClientId}'.
-
The authorization request was rejected because the identity token used as a hint was issued to a different client.
@@ -2758,6 +2694,24 @@ This may indicate that the hashed entry is corrupted or malformed.
An error occurred while retrieving the configuration of the remote authorization server.
+
+ The authentication demand was rejected because the mandatory '{Parameter}' parameter was missing.
+
+
+ The authentication demand was rejected because the client application was not found: '{ClientId}'.
+
+
+ The authentication demand was rejected because the public client application '{ClientId}' was not allowed to use the client credentials grant.
+
+
+ The authentication demand was rejected because the public application '{ClientId}' was not allowed to send a client secret.
+
+
+ The authentication demand was rejected because the confidential application '{ClientId}' didn't specify a client secret.
+
+
+ The authentication demand was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.
+
https://documentation.openiddict.com/errors/{0}
diff --git a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs
index 76e12fd4..33fdbc40 100644
--- a/src/OpenIddict.Server/OpenIddictServerConfiguration.cs
+++ b/src/OpenIddict.Server/OpenIddictServerConfiguration.cs
@@ -178,7 +178,8 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions
- descriptor.ContextType == typeof(ValidateDeviceRequestContext) &&
+ (descriptor.ContextType == typeof(ValidateDeviceRequestContext) ||
+ descriptor.ContextType == typeof(ProcessAuthenticationContext)) &&
descriptor.Type == OpenIddictServerHandlerType.Custom &&
descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
@@ -186,7 +187,8 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions
- descriptor.ContextType == typeof(ValidateIntrospectionRequestContext) &&
+ (descriptor.ContextType == typeof(ValidateIntrospectionRequestContext) ||
+ descriptor.ContextType == typeof(ProcessAuthenticationContext)) &&
descriptor.Type == OpenIddictServerHandlerType.Custom &&
descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
@@ -202,7 +204,8 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions
- descriptor.ContextType == typeof(ValidateRevocationRequestContext) &&
+ (descriptor.ContextType == typeof(ValidateRevocationRequestContext) ||
+ descriptor.ContextType == typeof(ProcessAuthenticationContext)) &&
descriptor.Type == OpenIddictServerHandlerType.Custom &&
descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
@@ -210,7 +213,8 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions
- descriptor.ContextType == typeof(ValidateTokenRequestContext) &&
+ (descriptor.ContextType == typeof(ValidateTokenRequestContext) ||
+ descriptor.ContextType == typeof(ProcessAuthenticationContext)) &&
descriptor.Type == OpenIddictServerHandlerType.Custom &&
descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
@@ -232,16 +236,16 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions
descriptor.ContextType == typeof(ValidateTokenContext) &&
- descriptor.Type == OpenIddictServerHandlerType.Custom &&
- descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
+ descriptor.Type is OpenIddictServerHandlerType.Custom &&
+ descriptor.FilterTypes.All(static type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0096));
}
if (!options.Handlers.Exists(static descriptor =>
descriptor.ContextType == typeof(GenerateTokenContext) &&
- descriptor.Type == OpenIddictServerHandlerType.Custom &&
- descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
+ descriptor.Type is OpenIddictServerHandlerType.Custom &&
+ descriptor.FilterTypes.All(static type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0097));
}
@@ -249,11 +253,11 @@ public sealed class OpenIddictServerConfiguration : IPostConfigureOptions left.Order.CompareTo(right.Order));
+ options.Handlers.Sort(static (left, right) => left.Order.CompareTo(right.Order));
// Sort the encryption and signing credentials.
- options.EncryptionCredentials.Sort((left, right) => Compare(left.Key, right.Key));
- options.SigningCredentials.Sort((left, right) => Compare(left.Key, right.Key));
+ options.EncryptionCredentials.Sort(static (left, right) => Compare(left.Key, right.Key));
+ options.SigningCredentials.Sort(static (left, right) => Compare(left.Key, right.Key));
// Generate a key identifier for the encryption/signing keys that don't already have one.
foreach (var key in options.EncryptionCredentials.Select(credentials => credentials.Key)
diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.cs b/src/OpenIddict.Server/OpenIddictServerEvents.cs
index 9f214965..92a6d7a4 100644
--- a/src/OpenIddict.Server/OpenIddictServerEvents.cs
+++ b/src/OpenIddict.Server/OpenIddictServerEvents.cs
@@ -294,7 +294,7 @@ public static partial class OpenIddictServerEvents
///
/// Represents an event called when processing an authentication operation.
///
- public sealed class ProcessAuthenticationContext : BaseValidatingContext
+ public sealed class ProcessAuthenticationContext : BaseValidatingClientContext
{
///
/// Creates a new instance of the class.
diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
index c1c3f2a3..ff2b97bd 100644
--- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs
+++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
@@ -48,6 +48,7 @@ public static class OpenIddictServerExtensions
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
+ builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs b/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs
index 800419cd..e148a66a 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs
@@ -147,6 +147,23 @@ public static class OpenIddictServerHandlerFilters
}
}
+ ///
+ /// Represents a filter that excludes the associated handlers when no client secret is received.
+ ///
+ public sealed class RequireClientSecretParameter : IOpenIddictServerHandlerFilter
+ {
+ ///
+ public ValueTask IsActiveAsync(BaseContext context)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ return new(!string.IsNullOrEmpty(context.Transaction.Request?.ClientSecret));
+ }
+ }
+
///
/// Represents a filter that excludes the associated handlers if the request is not a configuration request.
///
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
index f35078be..1be1dacc 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
@@ -42,8 +42,8 @@ public static partial class OpenIddictServerHandlers
ValidateNonceParameter.Descriptor,
ValidatePromptParameter.Descriptor,
ValidateProofKeyForCodeExchangeParameters.Descriptor,
- ValidateClientId.Descriptor,
- ValidateClientType.Descriptor,
+ ValidateAuthentication.Descriptor,
+ ValidateResponseType.Descriptor,
ValidateClientRedirectUri.Descriptor,
ValidateScopes.Descriptor,
ValidateEndpointPermissions.Descriptor,
@@ -51,7 +51,6 @@ public static partial class OpenIddictServerHandlers
ValidateResponseTypePermissions.Descriptor,
ValidateScopePermissions.Descriptor,
ValidateProofKeyForCodeExchangeRequirement.Descriptor,
- ValidateToken.Descriptor,
ValidateAuthorizedParty.Descriptor,
/*
@@ -1019,25 +1018,21 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for rejecting authorization requests that use an invalid client_id.
- /// Note: this handler is not used when the degraded mode is enabled.
+ /// Contains the logic responsible for applying the authentication logic to authorization requests.
///
- public sealed class ValidateClientId : IOpenIddictServerHandler
+ public sealed class ValidateAuthentication : IOpenIddictServerHandler
{
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientId() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+ private readonly IOpenIddictServerDispatcher _dispatcher;
- public ValidateClientId(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
+ public ValidateAuthentication(IOpenIddictServerDispatcher dispatcher)
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .UseScopedHandler()
+ .UseScopedHandler()
.SetOrder(ValidateProofKeyForCodeExchangeParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -1050,20 +1045,36 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
+ var notification = new ProcessAuthenticationContext(context.Transaction);
+ await _dispatcher.DispatchAsync(notification);
+
+ // Store the context object in the transaction so it can be later retrieved by handlers
+ // that want to access the authentication result without triggering a new authentication flow.
+ context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
- if (application is null)
+ if (notification.IsRequestHandled)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6044), context.ClientId);
+ context.HandleRequest();
+ return;
+ }
- context.Reject(
- error: Errors.InvalidRequest,
- description: SR.FormatID2052(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2052));
+ else if (notification.IsRequestSkipped)
+ {
+ context.SkipRequest();
+ return;
+ }
+ else if (notification.IsRejected)
+ {
+ context.Reject(
+ error: notification.Error ?? Errors.InvalidRequest,
+ description: notification.ErrorDescription,
+ uri: notification.ErrorUri);
return;
}
+
+ // Attach the security principal extracted from the token to the validation context.
+ context.IdentityTokenHintPrincipal = notification.IdentityTokenPrincipal;
}
}
@@ -1073,13 +1084,13 @@ public static partial class OpenIddictServerHandlers
/// Note: this handler is not used when the degraded mode is enabled
/// or when response type permissions enforcement is not disabled.
///
- public sealed class ValidateClientType : IOpenIddictServerHandler
+ public sealed class ValidateResponseType : IOpenIddictServerHandler
{
private readonly IOpenIddictApplicationManager _applicationManager;
- public ValidateClientType() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+ public ValidateResponseType() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
- public ValidateClientType(IOpenIddictApplicationManager applicationManager)
+ public ValidateResponseType(IOpenIddictApplicationManager applicationManager)
=> _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
///
@@ -1088,8 +1099,8 @@ public static partial class OpenIddictServerHandlers
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientId.Descriptor.Order + 1_000)
+ .UseScopedHandler()
+ .SetOrder(ValidateAuthentication.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -1151,7 +1162,7 @@ public static partial class OpenIddictServerHandlers
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.AddFilter()
.UseScopedHandler()
- .SetOrder(ValidateClientType.Descriptor.Order + 1_000)
+ .SetOrder(ValidateResponseType.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -1635,67 +1646,6 @@ public static partial class OpenIddictServerHandlers
}
}
- ///
- /// Contains the logic responsible for validating the token(s) present in the request.
- ///
- public sealed class ValidateToken : IOpenIddictServerHandler
- {
- private readonly IOpenIddictServerDispatcher _dispatcher;
-
- public ValidateToken(IOpenIddictServerDispatcher dispatcher)
- => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseScopedHandler()
- .SetOrder(ValidateProofKeyForCodeExchangeRequirement.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateAuthorizationRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- var notification = new ProcessAuthenticationContext(context.Transaction);
- await _dispatcher.DispatchAsync(notification);
-
- // Store the context object in the transaction so it can be later retrieved by handlers
- // that want to access the authentication result without triggering a new authentication flow.
- context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
-
- if (notification.IsRequestHandled)
- {
- context.HandleRequest();
- return;
- }
-
- else if (notification.IsRequestSkipped)
- {
- context.SkipRequest();
- return;
- }
-
- else if (notification.IsRejected)
- {
- context.Reject(
- error: notification.Error ?? Errors.InvalidRequest,
- description: notification.ErrorDescription,
- uri: notification.ErrorUri);
- return;
- }
-
- // Attach the security principal extracted from the token to the validation context.
- context.IdentityTokenHintPrincipal = notification.IdentityTokenPrincipal;
- }
- }
-
///
/// Contains the logic responsible for rejecting authorization requests that specify an identity
/// token hint that cannot be used by the client application sending the authorization request.
@@ -1708,7 +1658,7 @@ public static partial class OpenIddictServerHandlers
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(ValidateToken.Descriptor.Order + 1_000)
+ .SetOrder(ValidateProofKeyForCodeExchangeRequirement.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
index d57c6b5c..63924eb7 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
@@ -33,12 +33,9 @@ public static partial class OpenIddictServerHandlers
/*
* Device request validation:
*/
- ValidateClientIdParameter.Descriptor,
ValidateScopeParameter.Descriptor,
ValidateScopes.Descriptor,
- ValidateClientId.Descriptor,
- ValidateClientType.Descriptor,
- ValidateClientSecret.Descriptor,
+ ValidateDeviceAuthentication.Descriptor,
ValidateEndpointPermissions.Descriptor,
ValidateGrantTypePermissions.Descriptor,
ValidateScopePermissions.Descriptor,
@@ -57,7 +54,7 @@ public static partial class OpenIddictServerHandlers
/*
* Verification request validation:
*/
- ValidateToken.Descriptor,
+ ValidateVerificationAuthentication.Descriptor,
/*
* Verification request handling:
@@ -332,47 +329,6 @@ public static partial class OpenIddictServerHandlers
}
}
- ///
- /// Contains the logic responsible for rejecting device requests that don't specify a client identifier.
- ///
- public sealed class ValidateClientIdParameter : IOpenIddictServerHandler
- {
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseSingletonHandler()
- .SetOrder(int.MinValue + 100_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public ValueTask HandleAsync(ValidateDeviceRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- // client_id is a required parameter and MUST cause an error when missing.
- // See https://tools.ietf.org/html/rfc8628#section-3.1 for more information.
- if (string.IsNullOrEmpty(context.ClientId))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6056), Parameters.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2029(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2029));
-
- return default;
- }
-
- return default;
- }
- }
-
///
/// Contains the logic responsible for rejecting device requests that don't specify a valid scope parameter.
///
@@ -384,7 +340,7 @@ public static partial class OpenIddictServerHandlers
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(ValidateClientIdParameter.Descriptor.Order + 1_000)
+ .SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -491,26 +447,21 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for rejecting device requests that use an invalid client_id.
- /// Note: this handler is not used when the degraded mode is enabled.
+ /// Contains the logic responsible for applying the authentication logic to device requests.
///
- public sealed class ValidateClientId : IOpenIddictServerHandler
+ public sealed class ValidateDeviceAuthentication : IOpenIddictServerHandler
{
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientId() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+ private readonly IOpenIddictServerDispatcher _dispatcher;
- public ValidateClientId(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
+ public ValidateDeviceAuthentication(IOpenIddictServerDispatcher dispatcher)
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
+ .UseScopedHandler()
.SetOrder(ValidateScopes.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -523,152 +474,31 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- // Retrieve the application details corresponding to the requested client_id.
- // If no entity can be found, this likely indicates that the client_id is invalid.
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
- if (application is null)
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6058), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2052(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2052));
-
- return;
- }
- }
- }
-
- ///
- /// Contains the logic responsible for rejecting device requests made by applications
- /// whose client type is not compatible with the requested grant type.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientType : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientType() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
-
- public ValidateClientType(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientId.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateDeviceRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
-
- if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
- {
- // Reject device requests containing a client_secret when the client is a public application.
- if (!string.IsNullOrEmpty(context.ClientSecret))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6059), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2053(Parameters.ClientSecret),
- uri: SR.FormatID8000(SR.ID2053));
-
- return;
- }
+ var notification = new ProcessAuthenticationContext(context.Transaction);
+ await _dispatcher.DispatchAsync(notification);
- return;
- }
+ // Store the context object in the transaction so it can be later retrieved by handlers
+ // that want to access the authentication result without triggering a new authentication flow.
+ context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
- // Confidential and hybrid applications MUST authenticate to protect them from impersonation attacks.
- if (string.IsNullOrEmpty(context.ClientSecret))
+ if (notification.IsRequestHandled)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6060), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2054(Parameters.ClientSecret),
- uri: SR.FormatID8000(SR.ID2054));
-
+ context.HandleRequest();
return;
}
- }
- }
- ///
- /// Contains the logic responsible for rejecting device requests specifying an invalid client secret.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientSecret : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientSecret() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
-
- public ValidateClientSecret(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientType.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateDeviceRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
-
- // If the application is a public client, don't validate the client secret.
- if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
+ else if (notification.IsRequestSkipped)
{
+ context.SkipRequest();
return;
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientSecret), SR.FormatID4000(Parameters.ClientSecret));
-
- if (!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
+ else if (notification.IsRejected)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6061), context.ClientId);
-
context.Reject(
- error: Errors.InvalidClient,
- description: SR.GetResourceString(SR.ID2055),
- uri: SR.FormatID8000(SR.ID2055));
-
+ error: notification.Error ?? Errors.InvalidRequest,
+ description: notification.ErrorDescription,
+ uri: notification.ErrorUri);
return;
}
}
@@ -697,7 +527,7 @@ public static partial class OpenIddictServerHandlers
.AddFilter()
.AddFilter()
.UseScopedHandler()
- .SetOrder(ValidateClientSecret.Descriptor.Order + 1_000)
+ .SetOrder(ValidateDeviceAuthentication.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -1131,13 +961,13 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for validating the token(s) present in the request.
+ /// Contains the logic responsible for applying the authentication logic to verification requests.
///
- public sealed class ValidateToken : IOpenIddictServerHandler
+ public sealed class ValidateVerificationAuthentication : IOpenIddictServerHandler
{
private readonly IOpenIddictServerDispatcher _dispatcher;
- public ValidateToken(IOpenIddictServerDispatcher dispatcher)
+ public ValidateVerificationAuthentication(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
@@ -1145,7 +975,7 @@ public static partial class OpenIddictServerHandlers
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseScopedHandler()
+ .UseScopedHandler()
.SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
index 6ddb26ad..70300f41 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
@@ -46,14 +46,11 @@ public static partial class OpenIddictServerHandlers
ValidateProofKeyForCodeExchangeParameters.Descriptor,
ValidateScopeParameter.Descriptor,
ValidateScopes.Descriptor,
- ValidateClientId.Descriptor,
- ValidateClientType.Descriptor,
- ValidateClientSecret.Descriptor,
+ ValidateAuthentication.Descriptor,
ValidateEndpointPermissions.Descriptor,
ValidateGrantTypePermissions.Descriptor,
ValidateScopePermissions.Descriptor,
ValidateProofKeyForCodeExchangeRequirement.Descriptor,
- ValidateToken.Descriptor,
ValidatePresenters.Descriptor,
ValidateRedirectUri.Descriptor,
ValidateCodeVerifier.Descriptor,
@@ -425,17 +422,12 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
- if (!string.IsNullOrEmpty(context.ClientId))
- {
- return default;
- }
-
- // At this stage, reject the token request unless the client identification requirement was disabled.
- // Independently of this setting, also reject grant_type=authorization_code requests that don't specify
- // a client_id, as the client identifier MUST be sent by the client application in the request body
- // if it cannot be inferred from the client authentication method (e.g the username when using basic).
+ // Reject grant_type=authorization_code requests that don't specify a client_id, as the client
+ // identifier MUST be sent by the client application in the request body if it cannot be
+ // inferred from the client authentication method (e.g the username when using basic).
+ //
// See https://tools.ietf.org/html/rfc6749#section-4.1.3 for more information.
- if (!context.Options.AcceptAnonymousClients || context.Request.IsAuthorizationCodeGrantType())
+ if (string.IsNullOrEmpty(context.ClientId) && context.Request.IsAuthorizationCodeGrantType())
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6077), Parameters.ClientId);
@@ -834,26 +826,21 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for rejecting token requests that use an invalid client_id.
- /// Note: this handler is not used when the degraded mode is enabled.
+ /// Contains the logic responsible for applying the authentication logic to token requests.
///
- public sealed class ValidateClientId : IOpenIddictServerHandler
+ public sealed class ValidateAuthentication : IOpenIddictServerHandler
{
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientId() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+ private readonly IOpenIddictServerDispatcher _dispatcher;
- public ValidateClientId(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
+ public ValidateAuthentication(IOpenIddictServerDispatcher dispatcher)
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
+ .UseScopedHandler()
.SetOrder(ValidateScopes.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -866,167 +853,38 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- // Retrieve the application details corresponding to the requested client_id.
- // If no entity can be found, this likely indicates that the client_id is invalid.
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
- if (application is null)
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6081), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2052(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2052));
-
- return;
- }
- }
- }
-
- ///
- /// Contains the logic responsible for rejecting token requests made by applications
- /// whose client type is not compatible with the requested grant type.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientType : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientType() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
-
- public ValidateClientType(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientId.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateTokenRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
-
- if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
- {
- // Public applications are not allowed to use the client credentials grant.
- if (context.Request.IsClientCredentialsGrantType())
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6082), context.Request.ClientId);
-
- context.Reject(
- error: Errors.UnauthorizedClient,
- description: SR.FormatID2043(Parameters.GrantType),
- uri: SR.FormatID8000(SR.ID2043));
-
- return;
- }
-
- // Reject token requests containing a client_secret when the client is a public application.
- if (!string.IsNullOrEmpty(context.ClientSecret))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6083), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2053(Parameters.ClientSecret),
- uri: SR.FormatID8000(SR.ID2053));
-
- return;
- }
+ var notification = new ProcessAuthenticationContext(context.Transaction);
+ await _dispatcher.DispatchAsync(notification);
- return;
- }
+ // Store the context object in the transaction so it can be later retrieved by handlers
+ // that want to access the authentication result without triggering a new authentication flow.
+ context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
- // Confidential and hybrid applications MUST authenticate to protect them from impersonation attacks.
- if (string.IsNullOrEmpty(context.ClientSecret))
+ if (notification.IsRequestHandled)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6084), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2054(Parameters.ClientSecret),
- uri: SR.FormatID8000(SR.ID2054));
-
+ context.HandleRequest();
return;
}
- }
- }
-
- ///
- /// Contains the logic responsible for rejecting token requests specifying an invalid client secret.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientSecret : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientSecret() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
-
- public ValidateClientSecret(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientType.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateTokenRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
-
- // If the application is a public client, don't validate the client secret.
- if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
+ else if (notification.IsRequestSkipped)
{
+ context.SkipRequest();
return;
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientSecret), SR.FormatID4000(Parameters.ClientSecret));
-
- if (!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
+ else if (notification.IsRejected)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6085), context.ClientId);
-
context.Reject(
- error: Errors.InvalidClient,
- description: SR.GetResourceString(SR.ID2055),
- uri: SR.FormatID8000(SR.ID2055));
-
+ error: notification.Error ?? Errors.InvalidRequest,
+ description: notification.ErrorDescription,
+ uri: notification.ErrorUri);
return;
}
+
+ // Attach the security principal extracted from the token to the validation context.
+ context.Principal = context.Request.IsAuthorizationCodeGrantType() ? notification.AuthorizationCodePrincipal :
+ context.Request.IsDeviceCodeGrantType() ? notification.DeviceCodePrincipal :
+ context.Request.IsRefreshTokenGrantType() ? notification.RefreshTokenPrincipal : null;
}
}
@@ -1053,7 +911,7 @@ public static partial class OpenIddictServerHandlers
.AddFilter()
.AddFilter()
.UseScopedHandler()
- .SetOrder(ValidateClientSecret.Descriptor.Order + 1_000)
+ .SetOrder(ValidateAuthentication.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -1285,76 +1143,6 @@ public static partial class OpenIddictServerHandlers
}
}
- ///
- /// Contains the logic responsible for validating the token(s) present in the request.
- ///
- public sealed class ValidateToken : IOpenIddictServerHandler
- {
- private readonly IOpenIddictServerDispatcher _dispatcher;
-
- public ValidateToken(IOpenIddictServerDispatcher dispatcher)
- => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseScopedHandler()
- .SetOrder(ValidateProofKeyForCodeExchangeRequirement.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateTokenRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- if (!context.Request.IsAuthorizationCodeGrantType() &&
- !context.Request.IsDeviceCodeGrantType() &&
- !context.Request.IsRefreshTokenGrantType())
- {
- return;
- }
-
- var notification = new ProcessAuthenticationContext(context.Transaction);
- await _dispatcher.DispatchAsync(notification);
-
- // Store the context object in the transaction so it can be later retrieved by handlers
- // that want to access the authentication result without triggering a new authentication flow.
- context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
-
- if (notification.IsRequestHandled)
- {
- context.HandleRequest();
- return;
- }
-
- else if (notification.IsRequestSkipped)
- {
- context.SkipRequest();
- return;
- }
-
- else if (notification.IsRejected)
- {
- context.Reject(
- error: notification.Error ?? Errors.InvalidRequest,
- description: notification.ErrorDescription,
- uri: notification.ErrorUri);
- return;
- }
-
- // Attach the security principal extracted from the token to the validation context.
- context.Principal = context.Request.IsAuthorizationCodeGrantType() ? notification.AuthorizationCodePrincipal :
- context.Request.IsDeviceCodeGrantType() ? notification.DeviceCodePrincipal :
- context.Request.IsRefreshTokenGrantType() ? notification.RefreshTokenPrincipal : null;
- }
- }
-
///
/// Contains the logic responsible for rejecting token requests that use an authorization code,
/// a device code or a refresh token that was issued for a different client application.
@@ -1367,7 +1155,7 @@ public static partial class OpenIddictServerHandlers
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(ValidateToken.Descriptor.Order + 1_000)
+ .SetOrder(ValidateProofKeyForCodeExchangeRequirement.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
index 5ccf7442..40148e74 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
@@ -35,12 +35,8 @@ public static partial class OpenIddictServerHandlers
* Introspection request validation:
*/
ValidateTokenParameter.Descriptor,
- ValidateClientIdParameter.Descriptor,
- ValidateClientId.Descriptor,
- ValidateClientType.Descriptor,
- ValidateClientSecret.Descriptor,
+ ValidateAuthentication.Descriptor,
ValidateEndpointPermissions.Descriptor,
- ValidateToken.Descriptor,
ValidateTokenType.Descriptor,
ValidateAuthorizedParty.Descriptor,
@@ -371,120 +367,22 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for rejecting introspection requests that don't specify a client identifier.
+ /// Contains the logic responsible for applying the authentication logic to introspection requests.
///
- public sealed class ValidateClientIdParameter : IOpenIddictServerHandler
+ public sealed class ValidateAuthentication : IOpenIddictServerHandler
{
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseSingletonHandler()
- .SetOrder(ValidateTokenParameter.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public ValueTask HandleAsync(ValidateIntrospectionRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- // At this stage, reject the introspection request unless the client identification requirement was disabled.
- if (!context.Options.AcceptAnonymousClients && string.IsNullOrEmpty(context.ClientId))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6098), Parameters.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2029(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2029));
-
- return default;
- }
-
- return default;
- }
- }
-
- ///
- /// Contains the logic responsible for rejecting introspection requests that use an invalid client_id.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientId : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientId() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
-
- public ValidateClientId(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientIdParameter.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateIntrospectionRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- // Retrieve the application details corresponding to the requested client_id.
- // If no entity can be found, this likely indicates that the client_id is invalid.
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
- if (application is null)
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6099), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2052(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2052));
-
- return;
- }
- }
- }
-
- ///
- /// Contains the logic responsible for rejecting introspection requests made by applications
- /// whose client type is not compatible with the presence or absence of a client secret.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientType : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientType() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+ private readonly IOpenIddictServerDispatcher _dispatcher;
- public ValidateClientType(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
+ public ValidateAuthentication(IOpenIddictServerDispatcher dispatcher)
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientId.Descriptor.Order + 1_000)
+ .UseScopedHandler()
+ .SetOrder(ValidateTokenParameter.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -496,101 +394,36 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
-
- if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
- {
- // Reject introspection requests containing a client_secret when the client is a public application.
- if (!string.IsNullOrEmpty(context.ClientSecret))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6100), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2053(Parameters.ClientSecret),
- uri: SR.FormatID8000(SR.ID2053));
-
- return;
- }
+ var notification = new ProcessAuthenticationContext(context.Transaction);
+ await _dispatcher.DispatchAsync(notification);
- return;
- }
+ // Store the context object in the transaction so it can be later retrieved by handlers
+ // that want to access the authentication result without triggering a new authentication flow.
+ context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
- // Confidential and hybrid applications MUST authenticate to protect them from impersonation attacks.
- if (string.IsNullOrEmpty(context.ClientSecret))
+ if (notification.IsRequestHandled)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6101), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2054(Parameters.ClientSecret),
- uri: SR.FormatID8000(SR.ID2054));
-
+ context.HandleRequest();
return;
}
- }
- }
- ///
- /// Contains the logic responsible for rejecting introspection requests specifying an invalid client secret.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientSecret : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientSecret() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
-
- public ValidateClientSecret(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientType.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateIntrospectionRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
-
- // If the application is a public client, don't validate the client secret.
- if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
+ else if (notification.IsRequestSkipped)
{
+ context.SkipRequest();
return;
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientSecret), SR.FormatID4000(Parameters.ClientSecret));
-
- if (!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
+ else if (notification.IsRejected)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6102), context.ClientId);
-
context.Reject(
- error: Errors.InvalidClient,
- description: SR.GetResourceString(SR.ID2055),
- uri: SR.FormatID8000(SR.ID2055));
-
+ error: notification.Error ?? Errors.InvalidRequest,
+ description: notification.ErrorDescription,
+ uri: notification.ErrorUri);
return;
}
+
+ // Attach the security principal extracted from the token to the validation context.
+ context.Principal = notification.GenericTokenPrincipal;
}
}
@@ -617,7 +450,7 @@ public static partial class OpenIddictServerHandlers
.AddFilter()
.AddFilter()
.UseScopedHandler()
- .SetOrder(ValidateClientSecret.Descriptor.Order + 1_000)
+ .SetOrder(ValidateAuthentication.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -649,67 +482,6 @@ public static partial class OpenIddictServerHandlers
}
}
- ///
- /// Contains the logic responsible for validating the token(s) present in the request.
- ///
- public sealed class ValidateToken : IOpenIddictServerHandler
- {
- private readonly IOpenIddictServerDispatcher _dispatcher;
-
- public ValidateToken(IOpenIddictServerDispatcher dispatcher)
- => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseScopedHandler()
- .SetOrder(ValidateEndpointPermissions.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateIntrospectionRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- var notification = new ProcessAuthenticationContext(context.Transaction);
- await _dispatcher.DispatchAsync(notification);
-
- // Store the context object in the transaction so it can be later retrieved by handlers
- // that want to access the authentication result without triggering a new authentication flow.
- context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
-
- if (notification.IsRequestHandled)
- {
- context.HandleRequest();
- return;
- }
-
- else if (notification.IsRequestSkipped)
- {
- context.SkipRequest();
- return;
- }
-
- else if (notification.IsRejected)
- {
- context.Reject(
- error: notification.Error ?? Errors.InvalidRequest,
- description: notification.ErrorDescription,
- uri: notification.ErrorUri);
- return;
- }
-
- // Attach the security principal extracted from the token to the validation context.
- context.Principal = notification.GenericTokenPrincipal;
- }
- }
-
///
/// Contains the logic responsible for rejecting introspection requests that specify an unsupported token.
///
@@ -721,7 +493,7 @@ public static partial class OpenIddictServerHandlers
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(ValidateToken.Descriptor.Order + 1_000)
+ .SetOrder(ValidateEndpointPermissions.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs
index cfbe50c5..7765d30a 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs
@@ -29,12 +29,8 @@ public static partial class OpenIddictServerHandlers
* Revocation request validation:
*/
ValidateTokenParameter.Descriptor,
- ValidateClientIdParameter.Descriptor,
- ValidateClientId.Descriptor,
- ValidateClientType.Descriptor,
- ValidateClientSecret.Descriptor,
+ ValidateAuthentication.Descriptor,
ValidateEndpointPermissions.Descriptor,
- ValidateToken.Descriptor,
ValidateTokenType.Descriptor,
ValidateAuthorizedParty.Descriptor,
@@ -318,120 +314,22 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for rejecting revocation requests that don't specify a client identifier.
+ /// Contains the logic responsible for applying the authentication logic to revocation requests.
///
- public sealed class ValidateClientIdParameter : IOpenIddictServerHandler
+ public sealed class ValidateAuthentication : IOpenIddictServerHandler
{
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseSingletonHandler()
- .SetOrder(ValidateTokenParameter.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public ValueTask HandleAsync(ValidateRevocationRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- // At this stage, reject the revocation request unless the client identification requirement was disabled.
- if (!context.Options.AcceptAnonymousClients && string.IsNullOrEmpty(context.ClientId))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6111), Parameters.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2029(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2029));
-
- return default;
- }
-
- return default;
- }
- }
-
- ///
- /// Contains the logic responsible for rejecting revocation requests that use an invalid client_id.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientId : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientId() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
-
- public ValidateClientId(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientIdParameter.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateRevocationRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- // Retrieve the application details corresponding to the requested client_id.
- // If no entity can be found, this likely indicates that the client_id is invalid.
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
- if (application is null)
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6112), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2052(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2052));
-
- return;
- }
- }
- }
-
- ///
- /// Contains the logic responsible for rejecting revocation requests made by applications
- /// whose client type is not compatible with the presence or absence of a client secret.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientType : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientType() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+ private readonly IOpenIddictServerDispatcher _dispatcher;
- public ValidateClientType(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
+ public ValidateAuthentication(IOpenIddictServerDispatcher dispatcher)
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientId.Descriptor.Order + 1_000)
+ .UseScopedHandler()
+ .SetOrder(ValidateTokenParameter.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -443,101 +341,36 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
-
- if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
- {
- // Reject revocation requests containing a client_secret when the client is a public application.
- if (!string.IsNullOrEmpty(context.ClientSecret))
- {
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6113), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2053(Parameters.ClientSecret),
- uri: SR.FormatID8000(SR.ID2053));
-
- return;
- }
+ var notification = new ProcessAuthenticationContext(context.Transaction);
+ await _dispatcher.DispatchAsync(notification);
- return;
- }
+ // Store the context object in the transaction so it can be later retrieved by handlers
+ // that want to access the authentication result without triggering a new authentication flow.
+ context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
- // Confidential and hybrid applications MUST authenticate to protect them from impersonation attacks.
- if (string.IsNullOrEmpty(context.ClientSecret))
+ if (notification.IsRequestHandled)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6114), context.ClientId);
-
- context.Reject(
- error: Errors.InvalidClient,
- description: SR.FormatID2054(Parameters.ClientSecret),
- uri: SR.FormatID8000(SR.ID2054));
-
+ context.HandleRequest();
return;
}
- }
- }
- ///
- /// Contains the logic responsible for rejecting revocation requests specifying an invalid client secret.
- /// Note: this handler is not used when the degraded mode is enabled.
- ///
- public sealed class ValidateClientSecret : IOpenIddictServerHandler
- {
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientSecret() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
-
- public ValidateClientSecret(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- .AddFilter()
- .UseScopedHandler()
- .SetOrder(ValidateClientType.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateRevocationRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
-
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
-
- // If the application is a public client, don't validate the client secret.
- if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
+ else if (notification.IsRequestSkipped)
{
+ context.SkipRequest();
return;
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientSecret), SR.FormatID4000(Parameters.ClientSecret));
-
- if (!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
+ else if (notification.IsRejected)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6115), context.ClientId);
-
context.Reject(
- error: Errors.InvalidClient,
- description: SR.GetResourceString(SR.ID2055),
- uri: SR.FormatID8000(SR.ID2055));
-
+ error: notification.Error ?? Errors.InvalidRequest,
+ description: notification.ErrorDescription,
+ uri: notification.ErrorUri);
return;
}
+
+ // Attach the security principal extracted from the token to the validation context.
+ context.Principal = notification.GenericTokenPrincipal;
}
}
@@ -564,7 +397,7 @@ public static partial class OpenIddictServerHandlers
.AddFilter()
.AddFilter()
.UseScopedHandler()
- .SetOrder(ValidateClientSecret.Descriptor.Order + 1_000)
+ .SetOrder(ValidateAuthentication.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -596,67 +429,6 @@ public static partial class OpenIddictServerHandlers
}
}
- ///
- /// Contains the logic responsible for validating the token(s) present in the request.
- ///
- public sealed class ValidateToken : IOpenIddictServerHandler
- {
- private readonly IOpenIddictServerDispatcher _dispatcher;
-
- public ValidateToken(IOpenIddictServerDispatcher dispatcher)
- => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseScopedHandler()
- .SetOrder(ValidateEndpointPermissions.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateRevocationRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- var notification = new ProcessAuthenticationContext(context.Transaction);
- await _dispatcher.DispatchAsync(notification);
-
- // Store the context object in the transaction so it can be later retrieved by handlers
- // that want to access the authentication result without triggering a new authentication flow.
- context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
-
- if (notification.IsRequestHandled)
- {
- context.HandleRequest();
- return;
- }
-
- else if (notification.IsRequestSkipped)
- {
- context.SkipRequest();
- return;
- }
-
- else if (notification.IsRejected)
- {
- context.Reject(
- error: notification.Error ?? Errors.InvalidRequest,
- description: notification.ErrorDescription,
- uri: notification.ErrorUri);
- return;
- }
-
- // Attach the security principal extracted from the token to the validation context.
- context.Principal = notification.GenericTokenPrincipal;
- }
- }
-
///
/// Contains the logic responsible for rejecting revocation requests that specify an unsupported token.
///
@@ -668,7 +440,7 @@ public static partial class OpenIddictServerHandlers
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(ValidateToken.Descriptor.Order + 1_000)
+ .SetOrder(ValidateEndpointPermissions.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
index 50d2b58d..6ff8c03a 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
@@ -33,10 +33,9 @@ public static partial class OpenIddictServerHandlers
* Logout request validation:
*/
ValidatePostLogoutRedirectUriParameter.Descriptor,
- ValidateClientId.Descriptor,
+ ValidateAuthentication.Descriptor,
ValidateClientPostLogoutRedirectUri.Descriptor,
ValidateEndpointPermissions.Descriptor,
- ValidateToken.Descriptor,
ValidateAuthorizedParty.Descriptor,
/*
@@ -375,30 +374,21 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for rejecting logout requests
- /// that use an invalid client_id, if one was explicitly specified.
- /// Note: this handler is not used when the degraded mode is enabled.
+ /// Contains the logic responsible for applying the authentication logic to logout requests.
///
- public sealed class ValidateClientId : IOpenIddictServerHandler
+ public sealed class ValidateAuthentication : IOpenIddictServerHandler
{
- private readonly IOpenIddictApplicationManager _applicationManager;
-
- public ValidateClientId() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+ private readonly IOpenIddictServerDispatcher _dispatcher;
- public ValidateClientId(IOpenIddictApplicationManager applicationManager)
- => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
+ public ValidateAuthentication(IOpenIddictServerDispatcher dispatcher)
+ => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .AddFilter()
- // Note: support for the client_id parameter was only added in the second draft of the
- // https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout specification
- // and is optional. As such, the client identifier is only validated if it was specified.
- .AddFilter()
- .UseScopedHandler()
+ .UseScopedHandler()
.SetOrder(ValidatePostLogoutRedirectUriParameter.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -411,20 +401,36 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
- Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
+ var notification = new ProcessAuthenticationContext(context.Transaction);
+ await _dispatcher.DispatchAsync(notification);
+
+ // Store the context object in the transaction so it can be later retrieved by handlers
+ // that want to access the authentication result without triggering a new authentication flow.
+ context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
- var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
- if (application is null)
+ if (notification.IsRequestHandled)
{
- context.Logger.LogInformation(SR.GetResourceString(SR.ID6196), context.ClientId);
+ context.HandleRequest();
+ return;
+ }
- context.Reject(
- error: Errors.InvalidRequest,
- description: SR.FormatID2052(Parameters.ClientId),
- uri: SR.FormatID8000(SR.ID2052));
+ else if (notification.IsRequestSkipped)
+ {
+ context.SkipRequest();
+ return;
+ }
+ else if (notification.IsRejected)
+ {
+ context.Reject(
+ error: notification.Error ?? Errors.InvalidRequest,
+ description: notification.ErrorDescription,
+ uri: notification.ErrorUri);
return;
}
+
+ // Attach the security principal extracted from the token to the validation context.
+ context.IdentityTokenHintPrincipal = notification.IdentityTokenPrincipal;
}
}
@@ -450,7 +456,7 @@ public static partial class OpenIddictServerHandlers
.AddFilter()
.AddFilter()
.UseScopedHandler()
- .SetOrder(ValidateClientId.Descriptor.Order + 1_000)
+ .SetOrder(ValidateAuthentication.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -629,67 +635,6 @@ public static partial class OpenIddictServerHandlers
}
}
- ///
- /// Contains the logic responsible for validating the token(s) present in the request.
- ///
- public sealed class ValidateToken : IOpenIddictServerHandler
- {
- private readonly IOpenIddictServerDispatcher _dispatcher;
-
- public ValidateToken(IOpenIddictServerDispatcher dispatcher)
- => _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
-
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictServerHandlerDescriptor Descriptor { get; }
- = OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseScopedHandler()
- .SetOrder(ValidateEndpointPermissions.Descriptor.Order + 1_000)
- .SetType(OpenIddictServerHandlerType.BuiltIn)
- .Build();
-
- ///
- public async ValueTask HandleAsync(ValidateLogoutRequestContext context)
- {
- if (context is null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- var notification = new ProcessAuthenticationContext(context.Transaction);
- await _dispatcher.DispatchAsync(notification);
-
- // Store the context object in the transaction so it can be later retrieved by handlers
- // that want to access the authentication result without triggering a new authentication flow.
- context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
-
- if (notification.IsRequestHandled)
- {
- context.HandleRequest();
- return;
- }
-
- else if (notification.IsRequestSkipped)
- {
- context.SkipRequest();
- return;
- }
-
- else if (notification.IsRejected)
- {
- context.Reject(
- error: notification.Error ?? Errors.InvalidRequest,
- description: notification.ErrorDescription,
- uri: notification.ErrorUri);
- return;
- }
-
- // Attach the security principal extracted from the token to the validation context.
- context.IdentityTokenHintPrincipal = notification.IdentityTokenPrincipal;
- }
- }
-
///
/// Contains the logic responsible for rejecting logout requests that specify an identity
/// token hint that cannot be used by the client application sending the logout request.
@@ -717,7 +662,7 @@ public static partial class OpenIddictServerHandlers
new ValidateAuthorizedParty(provider.GetService() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0016)));
})
- .SetOrder(ValidateToken.Descriptor.Order + 1_000)
+ .SetOrder(ValidateEndpointPermissions.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs
index 0a6e03ef..bf4b5252 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs
@@ -30,7 +30,7 @@ public static partial class OpenIddictServerHandlers
* Userinfo request validation:
*/
ValidateAccessTokenParameter.Descriptor,
- ValidateToken.Descriptor,
+ ValidateAuthentication.Descriptor,
/*
* Userinfo request handling:
@@ -342,13 +342,13 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for validating the token(s) present in the request.
+ /// Contains the logic responsible for applying the authentication logic to userinfo requests.
///
- public sealed class ValidateToken : IOpenIddictServerHandler
+ public sealed class ValidateAuthentication : IOpenIddictServerHandler
{
private readonly IOpenIddictServerDispatcher _dispatcher;
- public ValidateToken(IOpenIddictServerDispatcher dispatcher)
+ public ValidateAuthentication(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
///
@@ -356,7 +356,7 @@ public static partial class OpenIddictServerHandlers
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseScopedHandler()
+ .UseScopedHandler()
.SetOrder(ValidateAccessTokenParameter.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
index 5f175608..419c6aba 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
@@ -34,6 +34,9 @@ public static partial class OpenIddictServerHandlers
EvaluateValidatedTokens.Descriptor,
ResolveValidatedTokens.Descriptor,
ValidateRequiredTokens.Descriptor,
+ ValidateClientId.Descriptor,
+ ValidateClientType.Descriptor,
+ ValidateClientSecret.Descriptor,
ValidateAccessToken.Descriptor,
ValidateAuthorizationCode.Descriptor,
ValidateDeviceCode.Descriptor,
@@ -52,8 +55,8 @@ public static partial class OpenIddictServerHandlers
AttachCustomChallengeParameters.Descriptor,
/*
- * Sign-in processing:
- */
+ * Sign-in processing:
+ */
ValidateSignInDemand.Descriptor,
RedeemTokenEntry.Descriptor,
RestoreInternalClaims.Descriptor,
@@ -224,24 +227,16 @@ public static partial class OpenIddictServerHandlers
throw new ArgumentNullException(nameof(context));
}
- switch (context.EndpointType)
+ return context.EndpointType switch
{
- case OpenIddictServerEndpointType.Authorization:
- case OpenIddictServerEndpointType.Introspection:
- case OpenIddictServerEndpointType.Logout:
- case OpenIddictServerEndpointType.Revocation:
- case OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType():
- case OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType():
- case OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType():
- case OpenIddictServerEndpointType.Userinfo:
- case OpenIddictServerEndpointType.Verification:
- return default;
+ OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Device or
+ OpenIddictServerEndpointType.Introspection or OpenIddictServerEndpointType.Logout or
+ OpenIddictServerEndpointType.Revocation or OpenIddictServerEndpointType.Token or
+ OpenIddictServerEndpointType.Userinfo or OpenIddictServerEndpointType.Verification
+ => default,
- case OpenIddictServerEndpointType.Token:
- throw new InvalidOperationException(SR.GetResourceString(SR.ID0001));
-
- default: throw new InvalidOperationException(SR.GetResourceString(SR.ID0002));
- }
+ _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0002)),
+ };
}
}
@@ -361,7 +356,7 @@ public static partial class OpenIddictServerHandlers
}
///
- /// Contains the logic responsible for resolving the token from the incoming request.
+ /// Contains the logic responsible for resolving the tokens from the incoming request.
///
public sealed class ResolveValidatedTokens : IOpenIddictServerHandler
{
@@ -490,6 +485,273 @@ public static partial class OpenIddictServerHandlers
}
}
+ ///
+ /// Contains the logic responsible for rejecting authentication demands that use an invalid client_id.
+ ///
+ public sealed class ValidateClientId : IOpenIddictServerHandler
+ {
+ private readonly IOpenIddictApplicationManager? _applicationManager;
+
+ public ValidateClientId(IOpenIddictApplicationManager? applicationManager = null)
+ => _applicationManager = applicationManager;
+
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictServerHandlerDescriptor Descriptor { get; }
+ = OpenIddictServerHandlerDescriptor.CreateBuilder()
+ .UseScopedHandler(static provider =>
+ {
+ // Note: the application manager is only resolved if the degraded mode was not enabled to ensure
+ // invalid core configuration exceptions are not thrown even if the managers were registered.
+ var options = provider.GetRequiredService>().CurrentValue;
+
+ return options.EnableDegradedMode ?
+ new ValidateClientId() :
+ new ValidateClientId(provider.GetService() ??
+ throw new InvalidOperationException(SR.GetResourceString(SR.ID0016)));
+ })
+ .SetOrder(ValidateRequiredTokens.Descriptor.Order + 1_000)
+ .SetType(OpenIddictServerHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public async ValueTask HandleAsync(ProcessAuthenticationContext context)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ // Don't validate the client identifier on endpoint that don't support client identification.
+ if (context.EndpointType is OpenIddictServerEndpointType.Userinfo or OpenIddictServerEndpointType.Verification)
+ {
+ return;
+ }
+
+ if (string.IsNullOrEmpty(context.ClientId))
+ {
+ switch (context.EndpointType)
+ {
+ // Note: support for the client_id parameter was only added in the second draft of the
+ // https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout specification
+ // and is optional. As such, the client identifier is only validated if it was specified.
+ case OpenIddictServerEndpointType.Logout:
+ return;
+
+ case OpenIddictServerEndpointType.Introspection when context.Options.AcceptAnonymousClients:
+ case OpenIddictServerEndpointType.Revocation when context.Options.AcceptAnonymousClients:
+ case OpenIddictServerEndpointType.Token when context.Options.AcceptAnonymousClients:
+ return;
+ }
+
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6220), Parameters.ClientId);
+
+ context.Reject(
+ error: Errors.InvalidClient,
+ description: SR.FormatID2029(Parameters.ClientId),
+ uri: SR.FormatID8000(SR.ID2029));
+
+ return;
+ }
+
+ if (!context.Options.EnableDegradedMode)
+ {
+ if (_applicationManager is null)
+ {
+ throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+ }
+
+ // Retrieve the application details corresponding to the requested client_id.
+ // If no entity can be found, this likely indicates that the client_id is invalid.
+ var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
+ if (application is null)
+ {
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6221), context.ClientId);
+
+ context.Reject(
+ error: context.EndpointType switch
+ {
+ // For non-interactive endpoints, return "invalid_client" instead of "invalid_request".
+ OpenIddictServerEndpointType.Device or OpenIddictServerEndpointType.Introspection or
+ OpenIddictServerEndpointType.Revocation or OpenIddictServerEndpointType.Token
+ => Errors.InvalidClient,
+
+ _ => Errors.InvalidRequest
+ },
+ description: SR.FormatID2052(Parameters.ClientId),
+ uri: SR.FormatID8000(SR.ID2052));
+
+ return;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Contains the logic responsible for rejecting authentication demands made by applications
+ /// whose client type is not compatible with the presence of client credentials.
+ /// Note: this handler is not used when the degraded mode is enabled.
+ ///
+ public sealed class ValidateClientType : IOpenIddictServerHandler
+ {
+ private readonly IOpenIddictApplicationManager _applicationManager;
+
+ public ValidateClientType() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+
+ public ValidateClientType(IOpenIddictApplicationManager applicationManager)
+ => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
+
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictServerHandlerDescriptor Descriptor { get; }
+ = OpenIddictServerHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .AddFilter()
+ .UseScopedHandler()
+ .SetOrder(ValidateClientId.Descriptor.Order + 1_000)
+ .SetType(OpenIddictServerHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public async ValueTask HandleAsync(ProcessAuthenticationContext context)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
+
+ // Don't validate the client type on endpoint that don't support client authentication.
+ if (context.EndpointType is OpenIddictServerEndpointType.Authorization or
+ OpenIddictServerEndpointType.Logout or
+ OpenIddictServerEndpointType.Userinfo or
+ OpenIddictServerEndpointType.Verification)
+ {
+ return;
+ }
+
+ var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
+ throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
+
+ if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
+ {
+ // Reject grant_type=client_credentials token requests if the application is a public client.
+ if (context.EndpointType is OpenIddictServerEndpointType.Token &&
+ context.Request.IsClientCredentialsGrantType())
+ {
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6222), context.Request.ClientId);
+
+ context.Reject(
+ error: Errors.UnauthorizedClient,
+ description: SR.FormatID2043(Parameters.GrantType),
+ uri: SR.FormatID8000(SR.ID2043));
+
+ return;
+ }
+
+ // Reject requests containing a client_secret when the client is a public application.
+ if (!string.IsNullOrEmpty(context.ClientSecret))
+ {
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6223), context.ClientId);
+
+ context.Reject(
+ error: Errors.InvalidClient,
+ description: SR.FormatID2053(Parameters.ClientSecret),
+ uri: SR.FormatID8000(SR.ID2053));
+
+ return;
+ }
+
+ return;
+ }
+
+ // Confidential and hybrid applications MUST authenticate to protect them from impersonation attacks.
+ if (string.IsNullOrEmpty(context.ClientSecret))
+ {
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6224), context.ClientId);
+
+ context.Reject(
+ error: Errors.InvalidClient,
+ description: SR.FormatID2054(Parameters.ClientSecret),
+ uri: SR.FormatID8000(SR.ID2054));
+
+ return;
+ }
+ }
+ }
+
+ ///
+ /// Contains the logic responsible for rejecting authentication demands specifying an invalid client secret.
+ /// Note: this handler is not used when the degraded mode is enabled.
+ ///
+ public sealed class ValidateClientSecret : IOpenIddictServerHandler
+ {
+ private readonly IOpenIddictApplicationManager _applicationManager;
+
+ public ValidateClientSecret() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
+
+ public ValidateClientSecret(IOpenIddictApplicationManager applicationManager)
+ => _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
+
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictServerHandlerDescriptor Descriptor { get; }
+ = OpenIddictServerHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .AddFilter()
+ .AddFilter()
+ .UseScopedHandler()
+ .SetOrder(ValidateClientType.Descriptor.Order + 1_000)
+ .SetType(OpenIddictServerHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public async ValueTask HandleAsync(ProcessAuthenticationContext context)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
+ Debug.Assert(!string.IsNullOrEmpty(context.ClientSecret), SR.FormatID4000(Parameters.ClientSecret));
+
+ // Don't validate the client secret on endpoint that don't support client authentication.
+ if (context.EndpointType is OpenIddictServerEndpointType.Authorization or
+ OpenIddictServerEndpointType.Logout or
+ OpenIddictServerEndpointType.Userinfo or
+ OpenIddictServerEndpointType.Verification)
+ {
+ return;
+ }
+
+ var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
+ throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
+
+ // If the application is a public client, don't validate the client secret.
+ if (await _applicationManager.HasClientTypeAsync(application, ClientTypes.Public))
+ {
+ return;
+ }
+
+ if (!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
+ {
+ context.Logger.LogInformation(SR.GetResourceString(SR.ID6225), context.ClientId);
+
+ context.Reject(
+ error: Errors.InvalidClient,
+ description: SR.GetResourceString(SR.ID2055),
+ uri: SR.FormatID8000(SR.ID2055));
+
+ return;
+ }
+ }
+ }
+
///
/// Contains the logic responsible for validating the access token resolved from the context.
///
@@ -507,7 +769,7 @@ public static partial class OpenIddictServerHandlers
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.AddFilter()
.UseScopedHandler()
- .SetOrder(ValidateRequiredTokens.Descriptor.Order + 1_000)
+ .SetOrder(ValidateClientSecret.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
index dfc86190..7671fd22 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
@@ -1846,7 +1846,31 @@ public abstract partial class OpenIddictServerIntegrationTests
await using var server = await CreateServerAsync(options =>
{
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token);
+ Assert.Equal(new[] { TokenTypeHints.AuthorizationCode }, context.ValidTokenTypes);
+
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(TokenTypeHints.AuthorizationCode)
+ .SetPresenters("Fabrikam")
+ .SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56")
+ .SetClaim(Claims.Subject, "Bob le Bricoleur");
+
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
options.Services.AddSingleton(manager);
+
+ options.SetDeviceEndpointUris(Array.Empty());
+ options.SetRevocationEndpointUris(Array.Empty());
+ options.Configure(options => options.GrantTypes.Remove(GrantTypes.DeviceCode));
+ options.DisableTokenStorage();
});
await using var client = await server.CreateClientAsync();
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs
index 91b5ba24..3f718904 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs
@@ -152,14 +152,12 @@ public abstract partial class OpenIddictServerIntegrationTests
}
[Fact]
- public async Task ValidateIntrospectionRequest_InvalidTokenCausesAnError()
+ public async Task ValidateIntrospectionRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
- options.EnableDegradedMode();
-
- options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
+ options.Configure(options => options.AcceptAnonymousClients = false);
});
await using var client = await server.CreateClientAsync();
@@ -167,86 +165,28 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
- Token = "SlAV32hkKG"
+ Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
- Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2004), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2004), response.ErrorUri);
+ Assert.Equal(Errors.InvalidClient, response.Error);
+ Assert.Equal(SR.FormatID2029(Parameters.ClientId), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri);
}
[Fact]
- public async Task ValidateIntrospectionRequest_ExpiredTokenCausesAnError()
+ public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCannotBeFound()
{
// Arrange
- await using var server = await CreateServerAsync(options =>
- {
- options.EnableDegradedMode();
-
- options.AddEventHandler(builder =>
- {
- builder.UseInlineHandler(context =>
- {
- Assert.Equal("SlAV32hkKG", context.Token);
-
- context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(TokenTypeHints.RefreshToken)
- .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1));
-
- return default;
- });
-
- builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
- });
-
- options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
- });
-
- await using var client = await server.CreateClientAsync();
-
- // Act
- var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
+ var manager = CreateApplicationManager(mock =>
{
- Token = "SlAV32hkKG",
- TokenTypeHint = TokenTypeHints.RefreshToken
+ mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(value: null);
});
- // Assert
- Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2018), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2018), response.ErrorUri);
- }
-
- [Theory]
- [InlineData(TokenTypeHints.AuthorizationCode)]
- [InlineData(TokenTypeHints.DeviceCode)]
- [InlineData(TokenTypeHints.IdToken)]
- [InlineData(TokenTypeHints.UserCode)]
- [InlineData("custom_token")]
- public async Task ValidateIntrospectionRequest_UnsupportedTokenTypeCausesAnError(string type)
- {
- // Arrange
await using var server = await CreateServerAsync(options =>
{
- options.EnableDegradedMode();
-
- options.AddEventHandler(builder =>
- {
- builder.UseInlineHandler(context =>
- {
- Assert.Equal("5HtRgAtc02", context.Token);
-
- context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(type);
-
- return default;
- });
-
- builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
- });
-
- options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
+ options.Services.AddSingleton(manager);
});
await using var client = await server.CreateClientAsync();
@@ -255,76 +195,47 @@ public abstract partial class OpenIddictServerIntegrationTests
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
ClientId = "Fabrikam",
- Token = "5HtRgAtc02"
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
+ Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
- Assert.Equal(Errors.UnsupportedTokenType, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2076), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2076), response.ErrorUri);
+ Assert.Equal(Errors.InvalidClient, response.Error);
+ Assert.Equal(SR.FormatID2052(Parameters.ClientId), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2052), response.ErrorUri);
+
+ Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.Once());
}
[Fact]
- public async Task ValidateIntrospectionRequest_AccessTokenCausesAnErrorWhenCallerIsNotAValidAudienceOrPresenter()
+ public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenEndpointPermissionIsNotGranted()
{
// Arrange
- await using var server = await CreateServerAsync(options =>
- {
- options.EnableDegradedMode();
-
- options.AddEventHandler(builder =>
- {
- builder.UseInlineHandler(context =>
- {
- Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token);
-
- context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(TokenTypeHints.AccessToken)
- .SetAudiences("AdventureWorks")
- .SetPresenters("Contoso");
-
- return default;
- });
-
- builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
- });
+ var application = new OpenIddictApplication();
- options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
- });
+ var manager = CreateApplicationManager(mock =>
+ {
+ mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(application);
- await using var client = await server.CreateClientAsync();
+ mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()))
+ .ReturnsAsync(true);
- // Act
- var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
- {
- ClientId = "Fabrikam",
- Token = "2YotnFZFEjr1zCsicMWpAA",
- TokenTypeHint = TokenTypeHints.AccessToken
+ mock.Setup(manager => manager.HasPermissionAsync(application,
+ Permissions.Endpoints.Introspection, It.IsAny()))
+ .ReturnsAsync(false);
});
- // Assert
- Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2077), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2077), response.ErrorUri);
- }
-
- [Fact]
- public async Task ValidateIntrospectionRequest_RefreshTokenCausesAnErrorWhenCallerIsNotAValidPresenter()
- {
- // Arrange
await using var server = await CreateServerAsync(options =>
{
- options.EnableDegradedMode();
-
options.AddEventHandler(builder =>
{
builder.UseInlineHandler(context =>
{
- Assert.Equal("8xLOxBtZp8", context.Token);
+ Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(TokenTypeHints.RefreshToken)
- .SetPresenters("Contoso");
+ .SetTokenType(TokenTypeHints.RefreshToken);
return default;
});
@@ -332,7 +243,9 @@ public abstract partial class OpenIddictServerIntegrationTests
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
- options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
+ options.Services.AddSingleton(manager);
+
+ options.Configure(options => options.IgnoreEndpointPermissions = false);
});
await using var client = await server.CreateClientAsync();
@@ -341,23 +254,37 @@ public abstract partial class OpenIddictServerIntegrationTests
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
ClientId = "Fabrikam",
- Token = "8xLOxBtZp8",
- TokenTypeHint = TokenTypeHints.RefreshToken
+ Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
- Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2077), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2077), response.ErrorUri);
+ Assert.Equal(Errors.UnauthorizedClient, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2075), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2075), response.ErrorUri);
+
+ Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce());
+ Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application,
+ Permissions.Endpoints.Introspection, It.IsAny()), Times.Once());
}
[Fact]
- public async Task ValidateIntrospectionRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired()
+ public async Task ValidateIntrospectionRequest_ClientSecretCannotBeUsedByPublicClients()
{
// Arrange
- await using var server = await CreateServerAsync(builder =>
+ var application = new OpenIddictApplication();
+
+ var manager = CreateApplicationManager(mock =>
{
- builder.Configure(options => options.AcceptAnonymousClients = false);
+ mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(application);
+
+ mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()))
+ .ReturnsAsync(true);
+ });
+
+ await using var server = await CreateServerAsync(options =>
+ {
+ options.Services.AddSingleton(manager);
});
await using var client = await server.CreateClientAsync();
@@ -365,23 +292,33 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
+ ClientId = "Fabrikam",
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
Assert.Equal(Errors.InvalidClient, response.Error);
- Assert.Equal(SR.FormatID2029(Parameters.ClientId), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri);
+ Assert.Equal(SR.FormatID2053(Parameters.ClientSecret), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2053), response.ErrorUri);
+
+ Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce());
+ Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()), Times.Once());
}
[Fact]
- public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCannotBeFound()
+ public async Task ValidateIntrospectionRequest_ClientSecretIsRequiredForNonPublicClients()
{
// Arrange
+ var application = new OpenIddictApplication();
+
var manager = CreateApplicationManager(mock =>
{
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()))
- .ReturnsAsync(value: null);
+ .ReturnsAsync(application);
+
+ mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()))
+ .ReturnsAsync(false);
});
await using var server = await CreateServerAsync(options =>
@@ -395,20 +332,21 @@ public abstract partial class OpenIddictServerIntegrationTests
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
ClientId = "Fabrikam",
- ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
+ ClientSecret = null,
Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
Assert.Equal(Errors.InvalidClient, response.Error);
- Assert.Equal(SR.FormatID2052(Parameters.ClientId), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2052), response.ErrorUri);
+ Assert.Equal(SR.FormatID2054(Parameters.ClientSecret), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2054), response.ErrorUri);
- Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.Once());
+ Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce());
+ Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()), Times.Once());
}
[Fact]
- public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenEndpointPermissionIsNotGranted()
+ public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCredentialsAreInvalid()
{
// Arrange
var application = new OpenIddictApplication();
@@ -419,18 +357,15 @@ public abstract partial class OpenIddictServerIntegrationTests
.ReturnsAsync(application);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()))
- .ReturnsAsync(true);
+ .ReturnsAsync(false);
- mock.Setup(manager => manager.HasPermissionAsync(application,
- Permissions.Endpoints.Introspection, It.IsAny()))
+ mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()))
.ReturnsAsync(false);
});
await using var server = await CreateServerAsync(options =>
{
options.Services.AddSingleton(manager);
-
- options.Configure(options => options.IgnoreEndpointPermissions = false);
});
await using var client = await server.CreateClientAsync();
@@ -439,37 +374,26 @@ public abstract partial class OpenIddictServerIntegrationTests
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
ClientId = "Fabrikam",
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
Token = "2YotnFZFEjr1zCsicMWpAA"
});
// Assert
- Assert.Equal(Errors.UnauthorizedClient, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2075), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2075), response.ErrorUri);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce());
- Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application,
- Permissions.Endpoints.Introspection, It.IsAny()), Times.Once());
+ Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()), Times.AtLeastOnce());
+ Mock.Get(manager).Verify(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()), Times.Once());
}
[Fact]
- public async Task ValidateIntrospectionRequest_ClientSecretCannotBeUsedByPublicClients()
+ public async Task ValidateIntrospectionRequest_InvalidTokenCausesAnError()
{
// Arrange
- var application = new OpenIddictApplication();
-
- var manager = CreateApplicationManager(mock =>
+ await using var server = await CreateServerAsync(options =>
{
- mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()))
- .ReturnsAsync(application);
-
- mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()))
- .ReturnsAsync(true);
- });
+ options.EnableDegradedMode();
- await using var server = await CreateServerAsync(builder =>
- {
- builder.Services.AddSingleton(manager);
+ options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
});
await using var client = await server.CreateClientAsync();
@@ -477,38 +401,86 @@ public abstract partial class OpenIddictServerIntegrationTests
// Act
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
- ClientId = "Fabrikam",
- ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
- Token = "2YotnFZFEjr1zCsicMWpAA"
+ Token = "SlAV32hkKG"
});
// Assert
- Assert.Equal(Errors.InvalidClient, response.Error);
- Assert.Equal(SR.FormatID2053(Parameters.ClientSecret), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2053), response.ErrorUri);
-
- Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce());
- Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()), Times.Once());
+ Assert.Equal(Errors.InvalidToken, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2004), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2004), response.ErrorUri);
}
[Fact]
- public async Task ValidateIntrospectionRequest_ClientSecretIsRequiredForNonPublicClients()
+ public async Task ValidateIntrospectionRequest_ExpiredTokenCausesAnError()
{
// Arrange
- var application = new OpenIddictApplication();
-
- var manager = CreateApplicationManager(mock =>
+ await using var server = await CreateServerAsync(options =>
{
- mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()))
- .ReturnsAsync(application);
+ options.EnableDegradedMode();
- mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()))
- .ReturnsAsync(false);
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("SlAV32hkKG", context.Token);
+
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(TokenTypeHints.RefreshToken)
+ .SetExpirationDate(DateTimeOffset.UtcNow - TimeSpan.FromDays(1));
+
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
+ options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
+ });
+
+ await using var client = await server.CreateClientAsync();
+
+ // Act
+ var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
+ {
+ Token = "SlAV32hkKG",
+ TokenTypeHint = TokenTypeHints.RefreshToken
});
- await using var server = await CreateServerAsync(builder =>
+ // Assert
+ Assert.Equal(Errors.InvalidToken, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2018), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2018), response.ErrorUri);
+ }
+
+ [Theory]
+ [InlineData(TokenTypeHints.AuthorizationCode)]
+ [InlineData(TokenTypeHints.DeviceCode)]
+ [InlineData(TokenTypeHints.IdToken)]
+ [InlineData(TokenTypeHints.UserCode)]
+ [InlineData("custom_token")]
+ public async Task ValidateIntrospectionRequest_UnsupportedTokenTypeCausesAnError(string type)
+ {
+ // Arrange
+ await using var server = await CreateServerAsync(options =>
{
- builder.Services.AddSingleton(manager);
+ options.EnableDegradedMode();
+
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("5HtRgAtc02", context.Token);
+
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(type);
+
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
+ options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
});
await using var client = await server.CreateClientAsync();
@@ -517,40 +489,84 @@ public abstract partial class OpenIddictServerIntegrationTests
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
ClientId = "Fabrikam",
- ClientSecret = null,
- Token = "2YotnFZFEjr1zCsicMWpAA"
+ Token = "5HtRgAtc02"
});
// Assert
- Assert.Equal(Errors.InvalidClient, response.Error);
- Assert.Equal(SR.FormatID2054(Parameters.ClientSecret), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2054), response.ErrorUri);
-
- Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce());
- Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()), Times.Once());
+ Assert.Equal(Errors.UnsupportedTokenType, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2076), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2076), response.ErrorUri);
}
[Fact]
- public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCredentialsAreInvalid()
+ public async Task ValidateIntrospectionRequest_AccessTokenCausesAnErrorWhenCallerIsNotAValidAudienceOrPresenter()
{
// Arrange
- var application = new OpenIddictApplication();
-
- var manager = CreateApplicationManager(mock =>
+ await using var server = await CreateServerAsync(options =>
{
- mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()))
- .ReturnsAsync(application);
+ options.EnableDegradedMode();
- mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()))
- .ReturnsAsync(false);
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token);
- mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()))
- .ReturnsAsync(false);
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(TokenTypeHints.AccessToken)
+ .SetAudiences("AdventureWorks")
+ .SetPresenters("Contoso");
+
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
+ options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
});
+ await using var client = await server.CreateClientAsync();
+
+ // Act
+ var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
+ {
+ ClientId = "Fabrikam",
+ Token = "2YotnFZFEjr1zCsicMWpAA",
+ TokenTypeHint = TokenTypeHints.AccessToken
+ });
+
+ // Assert
+ Assert.Equal(Errors.InvalidToken, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2077), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2077), response.ErrorUri);
+ }
+
+ [Fact]
+ public async Task ValidateIntrospectionRequest_RefreshTokenCausesAnErrorWhenCallerIsNotAValidPresenter()
+ {
+ // Arrange
await using var server = await CreateServerAsync(options =>
{
- options.Services.AddSingleton(manager);
+ options.EnableDegradedMode();
+
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("8xLOxBtZp8", context.Token);
+
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(TokenTypeHints.RefreshToken)
+ .SetPresenters("Contoso");
+
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
+ options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
});
await using var client = await server.CreateClientAsync();
@@ -559,15 +575,14 @@ public abstract partial class OpenIddictServerIntegrationTests
var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest
{
ClientId = "Fabrikam",
- ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
- Token = "2YotnFZFEjr1zCsicMWpAA"
+ Token = "8xLOxBtZp8",
+ TokenTypeHint = TokenTypeHints.RefreshToken
});
// Assert
-
- Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce());
- Mock.Get(manager).Verify(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny()), Times.AtLeastOnce());
- Mock.Get(manager).Verify(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()), Times.Once());
+ Assert.Equal(Errors.InvalidToken, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2077), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2077), response.ErrorUri);
}
[Theory]
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs
index 968fe1d2..a9f418bc 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs
@@ -7,6 +7,7 @@
using System.Net.Http;
using System.Security.Claims;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using Moq;
using Xunit;
using static OpenIddict.Server.OpenIddictServerEvents;
@@ -150,146 +151,13 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Equal(SR.FormatID8000(SR.ID2029), response.ErrorUri);
}
- [Theory]
- [InlineData(TokenTypeHints.AuthorizationCode)]
- [InlineData(TokenTypeHints.DeviceCode)]
- [InlineData(TokenTypeHints.IdToken)]
- [InlineData(TokenTypeHints.UserCode)]
- [InlineData("custom_token")]
- public async Task ValidateRevocationRequest_UnsupportedTokenTypeCausesAnError(string type)
- {
- // Arrange
- await using var server = await CreateServerAsync(options =>
- {
- options.EnableDegradedMode();
-
- options.AddEventHandler(builder =>
- {
- builder.UseInlineHandler(context =>
- {
- Assert.Equal("5HtRgAtc02", context.Token);
-
- context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(type);
-
- return default;
- });
-
- builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
- });
-
- options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
- });
-
- await using var client = await server.CreateClientAsync();
-
- // Act
- var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest
- {
- ClientId = "Fabrikam",
- Token = "5HtRgAtc02"
- });
-
- // Assert
- Assert.Equal(Errors.UnsupportedTokenType, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2079), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2079), response.ErrorUri);
- }
-
- [Fact]
- public async Task ValidateRevocationRequest_AccessTokenCausesAnErrorWhenCallerIsNotAValidAudienceOrPresenter()
- {
- // Arrange
- await using var server = await CreateServerAsync(options =>
- {
- options.EnableDegradedMode();
-
- options.AddEventHandler(builder =>
- {
- builder.UseInlineHandler(context =>
- {
- Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token);
-
- context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(TokenTypeHints.AccessToken)
- .SetAudiences("AdventureWorks")
- .SetPresenters("Contoso");
-
- return default;
- });
-
- builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
- });
-
- options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
- });
-
- await using var client = await server.CreateClientAsync();
-
- // Act
- var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest
- {
- ClientId = "Fabrikam",
- Token = "2YotnFZFEjr1zCsicMWpAA",
- TokenTypeHint = TokenTypeHints.AccessToken
- });
-
- // Assert
- Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2080), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2080), response.ErrorUri);
- }
-
- [Fact]
- public async Task ValidateRevocationRequest_RefreshTokenCausesAnErrorWhenCallerIsNotAValidPresenter()
- {
- // Arrange
- await using var server = await CreateServerAsync(options =>
- {
- options.EnableDegradedMode();
-
- options.AddEventHandler(builder =>
- {
- builder.UseInlineHandler(context =>
- {
- Assert.Equal("8xLOxBtZp8", context.Token);
-
- context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
- .SetTokenType(TokenTypeHints.RefreshToken)
- .SetPresenters("Contoso");
-
- return default;
- });
-
- builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
- });
-
- options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
- });
-
- await using var client = await server.CreateClientAsync();
-
- // Act
- var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest
- {
- ClientId = "Fabrikam",
- Token = "8xLOxBtZp8",
- TokenTypeHint = TokenTypeHints.RefreshToken
- });
-
- // Assert
- Assert.Equal(Errors.InvalidToken, response.Error);
- Assert.Equal(SR.GetResourceString(SR.ID2080), response.ErrorDescription);
- Assert.Equal(SR.FormatID8000(SR.ID2080), response.ErrorUri);
- }
-
[Fact]
public async Task ValidateRevocationRequest_RequestWithoutClientIdIsRejectedWhenClientIdentificationIsRequired()
{
// Arrange
- await using var server = await CreateServerAsync(builder =>
+ await using var server = await CreateServerAsync(options =>
{
- builder.Configure(options => options.AcceptAnonymousClients = false);
+ options.Configure(options => options.AcceptAnonymousClients = false);
});
await using var client = await server.CreateClientAsync();
@@ -317,9 +185,9 @@ public abstract partial class OpenIddictServerIntegrationTests
.ReturnsAsync(value: null);
});
- await using var server = await CreateServerAsync(builder =>
+ await using var server = await CreateServerAsync(options =>
{
- builder.Services.AddSingleton(manager);
+ options.Services.AddSingleton(manager);
});
await using var client = await server.CreateClientAsync();
@@ -359,11 +227,26 @@ public abstract partial class OpenIddictServerIntegrationTests
.ReturnsAsync(false);
});
- await using var server = await CreateServerAsync(builder =>
+ await using var server = await CreateServerAsync(options =>
{
- builder.Services.AddSingleton(manager);
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("SlAV32hkKG", context.Token);
+
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(TokenTypeHints.RefreshToken);
- builder.Configure(options => options.IgnoreEndpointPermissions = false);
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
+ options.Services.AddSingleton(manager);
+
+ options.Configure(options => options.IgnoreEndpointPermissions = false);
});
await using var client = await server.CreateClientAsync();
@@ -401,9 +284,9 @@ public abstract partial class OpenIddictServerIntegrationTests
.ReturnsAsync(true);
});
- await using var server = await CreateServerAsync(builder =>
+ await using var server = await CreateServerAsync(options =>
{
- builder.Services.AddSingleton(manager);
+ options.Services.AddSingleton(manager);
});
await using var client = await server.CreateClientAsync();
@@ -441,9 +324,9 @@ public abstract partial class OpenIddictServerIntegrationTests
.ReturnsAsync(false);
});
- await using var server = await CreateServerAsync(builder =>
+ await using var server = await CreateServerAsync(options =>
{
- builder.Services.AddSingleton(manager);
+ options.Services.AddSingleton(manager);
});
await using var client = await server.CreateClientAsync();
@@ -510,6 +393,139 @@ public abstract partial class OpenIddictServerIntegrationTests
Mock.Get(manager).Verify(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()), Times.Once());
}
+ [Theory]
+ [InlineData(TokenTypeHints.AuthorizationCode)]
+ [InlineData(TokenTypeHints.DeviceCode)]
+ [InlineData(TokenTypeHints.IdToken)]
+ [InlineData(TokenTypeHints.UserCode)]
+ [InlineData("custom_token")]
+ public async Task ValidateRevocationRequest_UnsupportedTokenTypeCausesAnError(string type)
+ {
+ // Arrange
+ await using var server = await CreateServerAsync(options =>
+ {
+ options.EnableDegradedMode();
+
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("5HtRgAtc02", context.Token);
+
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(type);
+
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
+ options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
+ });
+
+ await using var client = await server.CreateClientAsync();
+
+ // Act
+ var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest
+ {
+ ClientId = "Fabrikam",
+ Token = "5HtRgAtc02"
+ });
+
+ // Assert
+ Assert.Equal(Errors.UnsupportedTokenType, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2079), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2079), response.ErrorUri);
+ }
+
+ [Fact]
+ public async Task ValidateRevocationRequest_AccessTokenCausesAnErrorWhenCallerIsNotAValidAudienceOrPresenter()
+ {
+ // Arrange
+ await using var server = await CreateServerAsync(options =>
+ {
+ options.EnableDegradedMode();
+
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token);
+
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(TokenTypeHints.AccessToken)
+ .SetAudiences("AdventureWorks")
+ .SetPresenters("Contoso");
+
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
+ options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
+ });
+
+ await using var client = await server.CreateClientAsync();
+
+ // Act
+ var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest
+ {
+ ClientId = "Fabrikam",
+ Token = "2YotnFZFEjr1zCsicMWpAA",
+ TokenTypeHint = TokenTypeHints.AccessToken
+ });
+
+ // Assert
+ Assert.Equal(Errors.InvalidToken, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2080), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2080), response.ErrorUri);
+ }
+
+ [Fact]
+ public async Task ValidateRevocationRequest_RefreshTokenCausesAnErrorWhenCallerIsNotAValidPresenter()
+ {
+ // Arrange
+ await using var server = await CreateServerAsync(options =>
+ {
+ options.EnableDegradedMode();
+
+ options.AddEventHandler(builder =>
+ {
+ builder.UseInlineHandler(context =>
+ {
+ Assert.Equal("8xLOxBtZp8", context.Token);
+
+ context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
+ .SetTokenType(TokenTypeHints.RefreshToken)
+ .SetPresenters("Contoso");
+
+ return default;
+ });
+
+ builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
+ });
+
+ options.RemoveEventHandler(NormalizeErrorResponse.Descriptor);
+ });
+
+ await using var client = await server.CreateClientAsync();
+
+ // Act
+ var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest
+ {
+ ClientId = "Fabrikam",
+ Token = "8xLOxBtZp8",
+ TokenTypeHint = TokenTypeHints.RefreshToken
+ });
+
+ // Assert
+ Assert.Equal(Errors.InvalidToken, response.Error);
+ Assert.Equal(SR.GetResourceString(SR.ID2080), response.ErrorDescription);
+ Assert.Equal(SR.FormatID8000(SR.ID2080), response.ErrorUri);
+ }
+
[Theory]
[InlineData("custom_error", null, null)]
[InlineData("custom_error", "custom_description", null)]
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
index a119ccc8..65f61403 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
@@ -484,40 +484,6 @@ public abstract partial class OpenIddictServerIntegrationTests
Assert.Equal(SR.GetResourceString(SR.ID0002), exception.Message);
}
- [Fact]
- public async Task ProcessAuthentication_UnsupportedGrantTypeThrowsAnException()
- {
- // Arrange
- await using var server = await CreateServerAsync(options =>
- {
- options.EnableDegradedMode();
- options.SetTokenEndpointUris("/authenticate");
-
- options.AddEventHandler(builder =>
- builder.UseInlineHandler(context =>
- {
- context.SkipRequest();
-
- return default;
- }));
- });
-
- await using var client = await server.CreateClientAsync();
-
- // Act and assert
- var exception = await Assert.ThrowsAsync(delegate
- {
- return client.PostAsync("/authenticate", new OpenIddictRequest
- {
- GrantType = GrantTypes.Password,
- Username = "johndoe",
- Password = "A3ddj3w",
- });
- });
-
- Assert.Equal(SR.GetResourceString(SR.ID0001), exception.Message);
- }
-
[Fact]
public async Task ProcessAuthentication_MissingAccessTokenReturnsNull()
{
@@ -3880,22 +3846,13 @@ public abstract partial class OpenIddictServerIntegrationTests
options.AddEventHandler(builder =>
builder.UseInlineHandler(context => default));
- options.AddEventHandler(builder =>
- builder.UseInlineHandler(context => default));
-
- options.AddEventHandler(builder =>
- builder.UseInlineHandler(context => default));
-
options.AddEventHandler(builder =>
builder.UseInlineHandler(context => default));
- options.AddEventHandler(builder =>
- builder.UseInlineHandler(context => default));
-
- options.AddEventHandler(builder =>
+ options.AddEventHandler(builder =>
builder.UseInlineHandler(context => default));
- options.AddEventHandler(builder =>
+ options.AddEventHandler(builder =>
builder.UseInlineHandler(context => default));
options.AddEventHandler(builder =>