Skip to content

Commit

Permalink
Move the client authentication logic to the ProcessAuthentication event
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinchalet committed Sep 19, 2023
1 parent 66743fd commit a941660
Show file tree
Hide file tree
Showing 17 changed files with 933 additions and 1,626 deletions.
92 changes: 23 additions & 69 deletions src/OpenIddict.Abstractions/OpenIddictResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ID0001" xml:space="preserve">
<value>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).</value>
</data>
<data name="ID0002" xml:space="preserve">
<value>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.</value>
</data>
<data name="ID0003" xml:space="preserve">
Expand Down Expand Up @@ -426,19 +422,19 @@ To use key rollover, register both the new certificate and the old one in the cr
<value>No custom authorization request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateAuthorizationRequestContext&gt;' must be implemented to validate authorization requests (e.g to ensure the client_id and redirect_uri are valid).</value>
</data>
<data name="ID0090" xml:space="preserve">
<value>No custom device request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateDeviceRequestContext&gt;' must be implemented to validate device requests (e.g to ensure the client_id and client_secret are valid).</value>
<value>No custom device request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateDeviceRequestContext&gt;' (or 'IOpenIddictServerHandler&lt;ProcessAuthenticationContext&gt;') must be implemented to validate device requests (e.g to ensure the client_id and client_secret are valid).</value>
</data>
<data name="ID0091" xml:space="preserve">
<value>No custom introspection request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateIntrospectionRequestContext&gt;' must be implemented to validate introspection requests (e.g to ensure the client_id and client_secret are valid).</value>
<value>No custom introspection request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateIntrospectionRequestContext&gt;' (or 'IOpenIddictServerHandler&lt;ProcessAuthenticationContext&gt;') must be implemented to validate introspection requests (e.g to ensure the client_id and client_secret are valid).</value>
</data>
<data name="ID0092" xml:space="preserve">
<value>No custom logout request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateLogoutRequestContext&gt;' must be implemented to validate logout requests (e.g to ensure the post_logout_redirect_uri is valid).</value>
</data>
<data name="ID0093" xml:space="preserve">
<value>No custom revocation request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateRevocationRequestContext&gt;' must be implemented to validate revocation requests (e.g to ensure the client_id and client_secret are valid).</value>
<value>No custom revocation request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateRevocationRequestContext&gt;' (or 'IOpenIddictServerHandler&lt;ProcessAuthenticationContext&gt;') must be implemented to validate revocation requests (e.g to ensure the client_id and client_secret are valid).</value>
</data>
<data name="ID0094" xml:space="preserve">
<value>No custom token request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateTokenRequestContext&gt;' must be implemented to validate token requests (e.g to ensure the client_id and client_secret are valid).</value>
<value>No custom token request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateTokenRequestContext&gt;' (or 'IOpenIddictServerHandler&lt;ProcessAuthenticationContext&gt;') must be implemented to validate token requests (e.g to ensure the client_id and client_secret are valid).</value>
</data>
<data name="ID0095" xml:space="preserve">
<value>No custom verification request validation handler was found. When enabling the degraded mode, a custom 'IOpenIddictServerHandler&lt;ValidateVerificationRequestContext&gt;' must be implemented to validate verification requests (e.g to ensure the user_code is valid).</value>
Expand Down Expand Up @@ -2235,9 +2231,6 @@ The principal used to create the token contained the following claims: {Claims}.
<data name="ID6043" xml:space="preserve">
<value>The authorization request was rejected because the specified response type was not compatible with PKCE.</value>
</data>
<data name="ID6044" xml:space="preserve">
<value>The authorization request was rejected because the client application was not found: '{ClientId}'.</value>
</data>
<data name="ID6045" xml:space="preserve">
<value>The authorization request was rejected because the confidential application '{ClientId}' was not allowed to retrieve an access token from the authorization endpoint.</value>
</data>
Expand Down Expand Up @@ -2271,24 +2264,9 @@ The principal used to create the token contained the following claims: {Claims}.
<data name="ID6055" xml:space="preserve">
<value>The device request was successfully validated.</value>
</data>
<data name="ID6056" xml:space="preserve">
<value>The device request was rejected because the mandatory '{Parameter}' parameter was missing.</value>
</data>
<data name="ID6057" xml:space="preserve">
<value>The device request was rejected because invalid scopes were specified: {Scopes}.</value>
</data>
<data name="ID6058" xml:space="preserve">
<value>The device request was rejected because the client application was not found: '{ClientId}'.</value>
</data>
<data name="ID6059" xml:space="preserve">
<value>The device request was rejected because the public application '{ClientId}' was not allowed to send a client secret.</value>
</data>
<data name="ID6060" xml:space="preserve">
<value>The device request was rejected because the confidential application '{ClientId}' didn't specify a client secret.</value>
</data>
<data name="ID6061" xml:space="preserve">
<value>The device request was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.</value>
</data>
<data name="ID6062" xml:space="preserve">
<value>The device request was rejected because the application '{ClientId}' was not allowed to use the device endpoint.</value>
</data>
Expand Down Expand Up @@ -2346,21 +2324,6 @@ The principal used to create the token contained the following claims: {Claims}.
<data name="ID6080" xml:space="preserve">
<value>The token request was rejected because invalid scopes were specified: {Scopes}.</value>
</data>
<data name="ID6081" xml:space="preserve">
<value>The token request was rejected because the client application was not found: '{ClientId}'.</value>
</data>
<data name="ID6082" xml:space="preserve">
<value>The token request was rejected because the public client application '{ClientId}' was not allowed to use the client credentials grant.</value>
</data>
<data name="ID6083" xml:space="preserve">
<value>The token request was rejected because the public application '{ClientId}' was not allowed to send a client secret.</value>
</data>
<data name="ID6084" xml:space="preserve">
<value>The token request was rejected because the confidential application '{ClientId}' didn't specify a client secret.</value>
</data>
<data name="ID6085" xml:space="preserve">
<value>The token request was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.</value>
</data>
<data name="ID6086" xml:space="preserve">
<value>The token request was rejected because the application '{ClientId}' was not allowed to use the token endpoint.</value>
</data>
Expand Down Expand Up @@ -2400,18 +2363,6 @@ The principal used to create the token contained the following claims: {Claims}.
<data name="ID6098" xml:space="preserve">
<value>The introspection request was rejected because the mandatory '{Parameter}' parameter was missing.</value>
</data>
<data name="ID6099" xml:space="preserve">
<value>The introspection request was rejected because the client application was not found: '{ClientId}'.</value>
</data>
<data name="ID6100" xml:space="preserve">
<value>The introspection request was rejected because the public application '{ClientId}' was not allowed to send a client secret.</value>
</data>
<data name="ID6101" xml:space="preserve">
<value>The introspection request was rejected because the confidential application '{ClientId}' didn't specify a client secret.</value>
</data>
<data name="ID6102" xml:space="preserve">
<value>The introspection request was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.</value>
</data>
<data name="ID6103" xml:space="preserve">
<value>The introspection request was rejected because the application '{ClientId}' was not allowed to use the introspection endpoint.</value>
</data>
Expand Down Expand Up @@ -2439,18 +2390,6 @@ The principal used to create the token contained the following claims: {Claims}.
<data name="ID6111" xml:space="preserve">
<value>The revocation request was rejected because the mandatory '{Parameter}' parameter was missing.</value>
</data>
<data name="ID6112" xml:space="preserve">
<value>The revocation request was rejected because the client application was not found: '{ClientId}'.</value>
</data>
<data name="ID6113" xml:space="preserve">
<value>The revocation request was rejected because the public application '{ClientId}' was not allowed to send a client secret.</value>
</data>
<data name="ID6114" xml:space="preserve">
<value>The revocation request was rejected because the confidential application '{ClientId}' didn't specify a client secret.</value>
</data>
<data name="ID6115" xml:space="preserve">
<value>The revocation request was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.</value>
</data>
<data name="ID6116" xml:space="preserve">
<value>The revocation request was rejected because the application '{ClientId}' was not allowed to use the revocation endpoint.</value>
</data>
Expand Down Expand Up @@ -2689,9 +2628,6 @@ This may indicate that the hashed entry is corrupted or malformed.</value>
<data name="ID6195" xml:space="preserve">
<value>The userinfo response returned by {Uri} was successfully extracted: {Response}.</value>
</data>
<data name="ID6196" xml:space="preserve">
<value>The logout request was rejected because the client application was not found: '{ClientId}'.</value>
</data>
<data name="ID6197" xml:space="preserve">
<value>The authorization request was rejected because the identity token used as a hint was issued to a different client.</value>
</data>
Expand Down Expand Up @@ -2758,6 +2694,24 @@ This may indicate that the hashed entry is corrupted or malformed.</value>
<data name="ID6219" xml:space="preserve">
<value>An error occurred while retrieving the configuration of the remote authorization server.</value>
</data>
<data name="ID6220" xml:space="preserve">
<value>The authentication demand was rejected because the mandatory '{Parameter}' parameter was missing.</value>
</data>
<data name="ID6221" xml:space="preserve">
<value>The authentication demand was rejected because the client application was not found: '{ClientId}'.</value>
</data>
<data name="ID6222" xml:space="preserve">
<value>The authentication demand was rejected because the public client application '{ClientId}' was not allowed to use the client credentials grant.</value>
</data>
<data name="ID6223" xml:space="preserve">
<value>The authentication demand was rejected because the public application '{ClientId}' was not allowed to send a client secret.</value>
</data>
<data name="ID6224" xml:space="preserve">
<value>The authentication demand was rejected because the confidential application '{ClientId}' didn't specify a client secret.</value>
</data>
<data name="ID6225" xml:space="preserve">
<value>The authentication demand was rejected because the confidential application '{ClientId}' didn't specify valid client credentials.</value>
</data>
<data name="ID8000" xml:space="preserve">
<value>https://documentation.openiddict.com/errors/{0}</value>
</data>
Expand Down
26 changes: 15 additions & 11 deletions src/OpenIddict.Server/OpenIddictServerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,17 @@ public void PostConfigure(string? name, OpenIddictServerOptions options)
}

if (options.DeviceEndpointUris.Count is not 0 && !options.Handlers.Exists(static descriptor =>
descriptor.ContextType == typeof(ValidateDeviceRequestContext) &&
(descriptor.ContextType == typeof(ValidateDeviceRequestContext) ||
descriptor.ContextType == typeof(ProcessAuthenticationContext)) &&
descriptor.Type == OpenIddictServerHandlerType.Custom &&
descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0090));
}

if (options.IntrospectionEndpointUris.Count is not 0 && !options.Handlers.Exists(static descriptor =>
descriptor.ContextType == typeof(ValidateIntrospectionRequestContext) &&
(descriptor.ContextType == typeof(ValidateIntrospectionRequestContext) ||
descriptor.ContextType == typeof(ProcessAuthenticationContext)) &&
descriptor.Type == OpenIddictServerHandlerType.Custom &&
descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
Expand All @@ -202,15 +204,17 @@ public void PostConfigure(string? name, OpenIddictServerOptions options)
}

if (options.RevocationEndpointUris.Count is not 0 && !options.Handlers.Exists(static descriptor =>
descriptor.ContextType == typeof(ValidateRevocationRequestContext) &&
(descriptor.ContextType == typeof(ValidateRevocationRequestContext) ||
descriptor.ContextType == typeof(ProcessAuthenticationContext)) &&
descriptor.Type == OpenIddictServerHandlerType.Custom &&
descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0093));
}

if (options.TokenEndpointUris.Count is not 0 && !options.Handlers.Exists(static descriptor =>
descriptor.ContextType == typeof(ValidateTokenRequestContext) &&
(descriptor.ContextType == typeof(ValidateTokenRequestContext) ||
descriptor.ContextType == typeof(ProcessAuthenticationContext)) &&
descriptor.Type == OpenIddictServerHandlerType.Custom &&
descriptor.FilterTypes.All(type => !typeof(RequireDegradedModeDisabled).IsAssignableFrom(type))))
{
Expand All @@ -232,28 +236,28 @@ public void PostConfigure(string? name, OpenIddictServerOptions options)
{
if (!options.Handlers.Exists(static descriptor =>
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));
}
}
}

// Sort the handlers collection using the order associated with each handler.
options.Handlers.Sort((left, right) => 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)
Expand Down
2 changes: 1 addition & 1 deletion src/OpenIddict.Server/OpenIddictServerEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ public OpenIddictResponse Response
/// <summary>
/// Represents an event called when processing an authentication operation.
/// </summary>
public sealed class ProcessAuthenticationContext : BaseValidatingContext
public sealed class ProcessAuthenticationContext : BaseValidatingClientContext
{
/// <summary>
/// Creates a new instance of the <see cref="ProcessAuthenticationContext"/> class.
Expand Down
1 change: 1 addition & 0 deletions src/OpenIddict.Server/OpenIddictServerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static OpenIddictServerBuilder AddServer(this OpenIddictBuilder builder)
builder.Services.TryAddSingleton<RequireAuthorizationStorageEnabled>();
builder.Services.TryAddSingleton<RequireAuthorizationRequest>();
builder.Services.TryAddSingleton<RequireClientIdParameter>();
builder.Services.TryAddSingleton<RequireClientSecretParameter>();
builder.Services.TryAddSingleton<RequireConfigurationRequest>();
builder.Services.TryAddSingleton<RequireCryptographyRequest>();
builder.Services.TryAddSingleton<RequireDegradedModeDisabled>();
Expand Down
17 changes: 17 additions & 0 deletions src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,23 @@ public ValueTask<bool> IsActiveAsync(BaseContext context)
}
}

/// <summary>
/// Represents a filter that excludes the associated handlers when no client secret is received.
/// </summary>
public sealed class RequireClientSecretParameter : IOpenIddictServerHandlerFilter<BaseContext>
{
/// <inheritdoc/>
public ValueTask<bool> IsActiveAsync(BaseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}

return new(!string.IsNullOrEmpty(context.Transaction.Request?.ClientSecret));
}
}

/// <summary>
/// Represents a filter that excludes the associated handlers if the request is not a configuration request.
/// </summary>
Expand Down
Loading

0 comments on commit a941660

Please sign in to comment.