Browse Source

Revamp the handling of errors returned by the remote authorization server

pull/1530/head
Kévin Chalet 4 years ago
parent
commit
77a8a5c0c7
  1. 30
      src/OpenIddict.Abstractions/OpenIddictResources.resx
  2. 107
      src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs
  3. 58
      src/OpenIddict.Client/OpenIddictClientHandlers.Exchange.cs
  4. 61
      src/OpenIddict.Client/OpenIddictClientHandlers.Userinfo.cs
  5. 129
      src/OpenIddict.Client/OpenIddictClientHandlers.cs
  6. 107
      src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs
  7. 67
      src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs
  8. 9
      src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs
  9. 37
      src/OpenIddict.Validation/OpenIddictValidationHandlers.cs

30
src/OpenIddict.Abstractions/OpenIddictResources.resx

@ -1775,6 +1775,21 @@ Alternatively, you can disable the token storage feature by calling 'services.Ad
<data name="ID2143" xml:space="preserve"> <data name="ID2143" xml:space="preserve">
<value>An unsupported content encoding was returned by the remote server.</value> <value>An unsupported content encoding was returned by the remote server.</value>
</data> </data>
<data name="ID2144" xml:space="preserve">
<value>The configuration request was rejected by the remote server.</value>
</data>
<data name="ID2145" xml:space="preserve">
<value>The cryptography request was rejected by the remote server.</value>
</data>
<data name="ID2146" xml:space="preserve">
<value>The introspection request was rejected by the remote server.</value>
</data>
<data name="ID2147" xml:space="preserve">
<value>The token request was rejected by the remote server.</value>
</data>
<data name="ID2148" xml:space="preserve">
<value>The userinfo request was rejected by the remote server.</value>
</data>
<data name="ID4000" xml:space="preserve"> <data name="ID4000" xml:space="preserve">
<value>The '{0}' parameter shouldn't be null or empty at this point.</value> <value>The '{0}' parameter shouldn't be null or empty at this point.</value>
</data> </data>
@ -2392,6 +2407,21 @@ This may indicate that the hashed entry is corrupted or malformed.</value>
<data name="ID6202" xml:space="preserve"> <data name="ID6202" xml:space="preserve">
<value>Client validation failed because '{PostLogoutRedirectUri}' was not a valid post_logout_redirect_uri for {Client}.</value> <value>Client validation failed because '{PostLogoutRedirectUri}' was not a valid post_logout_redirect_uri for {Client}.</value>
</data> </data>
<data name="ID6203" xml:space="preserve">
<value>The configuration request was rejected by the remote authorization server: {Response}.</value>
</data>
<data name="ID6204" xml:space="preserve">
<value>The cryptography request was rejected by the remote authorization server: {Response}.</value>
</data>
<data name="ID6205" xml:space="preserve">
<value>The introspection request was rejected by the remote authorization server: {Response}.</value>
</data>
<data name="ID6206" xml:space="preserve">
<value>The token request was rejected by the remote authorization server: {Response}.</value>
</data>
<data name="ID6207" xml:space="preserve">
<value>The userinfo request was rejected by the remote authorization server: {Response}.</value>
</data>
<data name="ID8000" xml:space="preserve"> <data name="ID8000" xml:space="preserve">
<value>https://documentation.openiddict.com/errors/{0}</value> <value>https://documentation.openiddict.com/errors/{0}</value>
</data> </data>

107
src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs

@ -6,6 +6,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Client; namespace OpenIddict.Client;
@ -18,8 +19,8 @@ public static partial class OpenIddictClientHandlers
/* /*
* Configuration response handling: * Configuration response handling:
*/ */
HandleErrorResponse<HandleConfigurationResponseContext>.Descriptor,
ValidateWellKnownConfigurationParameters.Descriptor, ValidateWellKnownConfigurationParameters.Descriptor,
HandleConfigurationErrorResponse.Descriptor,
ValidateIssuer.Descriptor, ValidateIssuer.Descriptor,
ExtractAuthorizationEndpoint.Descriptor, ExtractAuthorizationEndpoint.Descriptor,
ExtractCryptographyEndpoint.Descriptor, ExtractCryptographyEndpoint.Descriptor,
@ -37,8 +38,8 @@ public static partial class OpenIddictClientHandlers
/* /*
* Cryptography response handling: * Cryptography response handling:
*/ */
HandleErrorResponse<HandleCryptographyResponseContext>.Descriptor,
ValidateWellKnownCryptographyParameters.Descriptor, ValidateWellKnownCryptographyParameters.Descriptor,
HandleCryptographyErrorResponse.Descriptor,
ExtractSigningKeys.Descriptor); ExtractSigningKeys.Descriptor);
/// <summary> /// <summary>
@ -52,7 +53,7 @@ public static partial class OpenIddictClientHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>()
.UseSingletonHandler<ValidateWellKnownConfigurationParameters>() .UseSingletonHandler<ValidateWellKnownConfigurationParameters>()
.SetOrder(HandleErrorResponse<HandleConfigurationResponseContext>.Descriptor.Order + 1_000) .SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -88,6 +89,10 @@ public static partial class OpenIddictClientHandlers
// JsonElement instance using the same value type as the original parameter value. // JsonElement instance using the same value type as the original parameter value.
static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{ {
// Error parameters MUST be formatted as unique strings:
Parameters.Error or Parameters.ErrorDescription or Parameters.ErrorUri
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
// The following parameters MUST be formatted as unique strings: // The following parameters MUST be formatted as unique strings:
Metadata.AuthorizationEndpoint or Metadata.AuthorizationEndpoint or
Metadata.EndSessionEndpoint or Metadata.EndSessionEndpoint or
@ -130,6 +135,49 @@ public static partial class OpenIddictClientHandlers
} }
} }
/// <summary>
/// Contains the logic responsible for surfacing potential errors from the configuration response.
/// </summary>
public class HandleConfigurationErrorResponse : IOpenIddictClientHandler<HandleConfigurationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>()
.UseSingletonHandler<HandleConfigurationErrorResponse>()
.SetOrder(ValidateWellKnownConfigurationParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleConfigurationResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// Note: the specification doesn't define a standard way to return an error other than
// returning a 4xx status code. That said, some implementations are known to return
// JSON payloads similar to standard errored token responses. For more information, see
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse.
if (!string.IsNullOrEmpty(context.Response.Error))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6203), context.Response);
context.Reject(
error: Errors.ServerError,
description: SR.GetResourceString(SR.ID2144),
uri: SR.FormatID8000(SR.ID2144));
return default;
}
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible for extracting the issuer from the discovery document. /// Contains the logic responsible for extracting the issuer from the discovery document.
/// </summary> /// </summary>
@ -141,7 +189,7 @@ public static partial class OpenIddictClientHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>()
.UseSingletonHandler<ValidateIssuer>() .UseSingletonHandler<ValidateIssuer>()
.SetOrder(ValidateWellKnownConfigurationParameters.Descriptor.Order + 1_000) .SetOrder(HandleConfigurationErrorResponse.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -721,7 +769,7 @@ public static partial class OpenIddictClientHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>()
.UseSingletonHandler<ValidateWellKnownCryptographyParameters>() .UseSingletonHandler<ValidateWellKnownCryptographyParameters>()
.SetOrder(HandleErrorResponse<HandleCryptographyResponseContext>.Descriptor.Order + 1_000) .SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -757,6 +805,10 @@ public static partial class OpenIddictClientHandlers
// JsonElement instance using the same value type as the original parameter value. // JsonElement instance using the same value type as the original parameter value.
static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{ {
// Error parameters MUST be formatted as unique strings:
Parameters.Error or Parameters.ErrorDescription or Parameters.ErrorUri
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
// The following parameters MUST be formatted as arrays of objects: // The following parameters MUST be formatted as arrays of objects:
JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element && JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element &&
element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element), element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element),
@ -780,6 +832,49 @@ public static partial class OpenIddictClientHandlers
} }
} }
/// <summary>
/// Contains the logic responsible for surfacing potential errors from the cryptography response.
/// </summary>
public class HandleCryptographyErrorResponse : IOpenIddictClientHandler<HandleCryptographyResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>()
.UseSingletonHandler<HandleCryptographyErrorResponse>()
.SetOrder(ValidateWellKnownCryptographyParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleCryptographyResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// Note: the specification doesn't define a standard way to return an error other than
// returning a 4xx status code. That said, some implementations are known to return
// JSON payloads similar to standard errored token responses. For more information, see
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse.
if (!string.IsNullOrEmpty(context.Response.Error))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6204), context.Response);
context.Reject(
error: Errors.ServerError,
description: SR.GetResourceString(SR.ID2145),
uri: SR.FormatID8000(SR.ID2145));
return default;
}
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible for extracting the signing keys from the JWKS document. /// Contains the logic responsible for extracting the signing keys from the JWKS document.
/// </summary> /// </summary>
@ -791,7 +886,7 @@ public static partial class OpenIddictClientHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>()
.UseSingletonHandler<ExtractSigningKeys>() .UseSingletonHandler<ExtractSigningKeys>()
.SetOrder(ValidateWellKnownCryptographyParameters.Descriptor.Order + 1_000) .SetOrder(HandleCryptographyErrorResponse.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();

58
src/OpenIddict.Client/OpenIddictClientHandlers.Exchange.cs

@ -6,6 +6,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Client; namespace OpenIddict.Client;
@ -17,8 +18,8 @@ public static partial class OpenIddictClientHandlers
/* /*
* Token response handling: * Token response handling:
*/ */
HandleErrorResponse<HandleTokenResponseContext>.Descriptor, ValidateWellKnownParameters.Descriptor,
ValidateWellKnownParameters.Descriptor); HandleErrorResponse.Descriptor);
/// <summary> /// <summary>
/// Contains the logic responsible for validating the well-known parameters contained in the token response. /// Contains the logic responsible for validating the well-known parameters contained in the token response.
@ -67,6 +68,10 @@ public static partial class OpenIddictClientHandlers
// JsonElement instance using the same value type as the original parameter value. // JsonElement instance using the same value type as the original parameter value.
static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{ {
// Error parameters MUST be formatted as unique strings:
Parameters.Error or Parameters.ErrorDescription or Parameters.ErrorUri
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
// The following parameters MUST be formatted as unique strings: // The following parameters MUST be formatted as unique strings:
Parameters.AccessToken or Parameters.IdToken or Parameters.RefreshToken Parameters.AccessToken or Parameters.IdToken or Parameters.RefreshToken
=> ((JsonElement) value).ValueKind is JsonValueKind.String, => ((JsonElement) value).ValueKind is JsonValueKind.String,
@ -79,5 +84,54 @@ public static partial class OpenIddictClientHandlers
}; };
} }
} }
/// <summary>
/// Contains the logic responsible for surfacing potential errors from the token response.
/// </summary>
public class HandleErrorResponse : IOpenIddictClientHandler<HandleTokenResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleTokenResponseContext>()
.UseSingletonHandler<HandleErrorResponse>()
.SetOrder(ValidateWellKnownParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleTokenResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// For more information, see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2.
if (!string.IsNullOrEmpty(context.Response.Error))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6206), context.Response);
context.Reject(
error: context.Response.Error switch
{
Errors.InvalidClient => Errors.InvalidRequest,
Errors.InvalidGrant => Errors.InvalidGrant,
Errors.InvalidScope => Errors.InvalidScope,
Errors.InvalidRequest => Errors.InvalidRequest,
Errors.UnauthorizedClient => Errors.UnauthorizedClient,
Errors.UnsupportedGrantType => Errors.UnsupportedGrantType,
_ => Errors.ServerError
},
description: SR.GetResourceString(SR.ID2147),
uri: SR.FormatID8000(SR.ID2147));
return default;
}
return default;
}
}
} }
} }

61
src/OpenIddict.Client/OpenIddictClientHandlers.Userinfo.cs

@ -7,6 +7,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Client; namespace OpenIddict.Client;
@ -18,21 +19,21 @@ public static partial class OpenIddictClientHandlers
/* /*
* Userinfo response handling: * Userinfo response handling:
*/ */
HandleErrorResponse<HandleUserinfoResponseContext>.Descriptor, ValidateWellKnownParameters.Descriptor,
ValidateWellKnownClaims.Descriptor, HandleErrorResponse.Descriptor,
PopulateClaims.Descriptor); PopulateClaims.Descriptor);
/// <summary> /// <summary>
/// Contains the logic responsible for validating the well-known parameters contained in the userinfo response. /// Contains the logic responsible for validating the well-known parameters contained in the userinfo response.
/// </summary> /// </summary>
public class ValidateWellKnownClaims : IOpenIddictClientHandler<HandleUserinfoResponseContext> public class ValidateWellKnownParameters : IOpenIddictClientHandler<HandleUserinfoResponseContext>
{ {
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
/// </summary> /// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleUserinfoResponseContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<HandleUserinfoResponseContext>()
.UseSingletonHandler<ValidateWellKnownClaims>() .UseSingletonHandler<ValidateWellKnownParameters>()
.SetOrder(int.MinValue + 100_000) .SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -75,6 +76,10 @@ public static partial class OpenIddictClientHandlers
// JsonElement instance using the same value type as the original parameter value. // JsonElement instance using the same value type as the original parameter value.
static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{ {
// Error parameters MUST be formatted as unique strings:
Parameters.Error or Parameters.ErrorDescription or Parameters.ErrorUri
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
// The following parameters MUST be formatted as unique strings: // The following parameters MUST be formatted as unique strings:
Claims.Subject => ((JsonElement) value).ValueKind is JsonValueKind.String, Claims.Subject => ((JsonElement) value).ValueKind is JsonValueKind.String,
@ -84,6 +89,52 @@ public static partial class OpenIddictClientHandlers
} }
} }
/// <summary>
/// Contains the logic responsible for surfacing potential errors from the userinfo response.
/// </summary>
public class HandleErrorResponse : IOpenIddictClientHandler<HandleUserinfoResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleUserinfoResponseContext>()
.UseSingletonHandler<HandleErrorResponse>()
.SetOrder(ValidateWellKnownParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleUserinfoResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// For more information, see https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError.
if (!string.IsNullOrEmpty(context.Response.Error))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6207), context.Response);
context.Reject(
error: context.Response.Error switch
{
Errors.InsufficientScope => Errors.InsufficientScope,
Errors.InvalidRequest => Errors.InvalidToken,
Errors.InvalidToken => Errors.InvalidToken,
_ => Errors.ServerError
},
description: SR.GetResourceString(SR.ID2148),
uri: SR.FormatID8000(SR.ID2148));
return default;
}
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible for extracting the claims from the introspection response. /// Contains the logic responsible for extracting the claims from the introspection response.
/// </summary> /// </summary>
@ -95,7 +146,7 @@ public static partial class OpenIddictClientHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleUserinfoResponseContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<HandleUserinfoResponseContext>()
.UseSingletonHandler<PopulateClaims>() .UseSingletonHandler<PopulateClaims>()
.SetOrder(ValidateWellKnownClaims.Descriptor.Order + 1_000) .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();

129
src/OpenIddict.Client/OpenIddictClientHandlers.cs

@ -10,7 +10,9 @@ using System.Diagnostics;
using System.Security.Claims; using System.Security.Claims;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Abstractions.OpenIddictExceptions;
#if !SUPPORTS_TIME_CONSTANT_COMPARISONS #if !SUPPORTS_TIME_CONSTANT_COMPARISONS
using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities;
@ -60,7 +62,6 @@ public static partial class OpenIddictClientHandlers
GenerateClientAssertionToken.Descriptor, GenerateClientAssertionToken.Descriptor,
AttachTokenRequestClientCredentials.Descriptor, AttachTokenRequestClientCredentials.Descriptor,
SendTokenRequest.Descriptor, SendTokenRequest.Descriptor,
ValidateTokenErrorParameters.Descriptor,
EvaluateValidatedBackchannelTokens.Descriptor, EvaluateValidatedBackchannelTokens.Descriptor,
ResolveValidatedBackchannelTokens.Descriptor, ResolveValidatedBackchannelTokens.Descriptor,
@ -80,7 +81,6 @@ public static partial class OpenIddictClientHandlers
EvaluateUserinfoRequest.Descriptor, EvaluateUserinfoRequest.Descriptor,
AttachUserinfoRequestParameters.Descriptor, AttachUserinfoRequestParameters.Descriptor,
SendUserinfoRequest.Descriptor, SendUserinfoRequest.Descriptor,
ValidateUserinfoErrorParameters.Descriptor,
EvaluateValidatedUserinfoToken.Descriptor, EvaluateValidatedUserinfoToken.Descriptor,
ValidateRequiredUserinfoToken.Descriptor, ValidateRequiredUserinfoToken.Descriptor,
ValidateUserinfoToken.Descriptor, ValidateUserinfoToken.Descriptor,
@ -2017,47 +2017,21 @@ public static partial class OpenIddictClientHandlers
throw new InvalidOperationException(SR.FormatID0301(Metadata.TokenEndpoint)); throw new InvalidOperationException(SR.FormatID0301(Metadata.TokenEndpoint));
} }
context.TokenResponse = await _service.SendTokenRequestAsync( try
context.Registration, context.TokenEndpoint, context.TokenRequest);
}
}
/// <summary>
/// Contains the logic responsible for rejecting errored token responses.
/// </summary>
public class ValidateTokenErrorParameters : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireTokenRequest>()
.UseSingletonHandler<ValidateTokenErrorParameters>()
.SetOrder(SendTokenRequest.Descriptor.Order + 1_000)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context)
{
if (context is null)
{ {
throw new ArgumentNullException(nameof(context)); context.TokenResponse = await _service.SendTokenRequestAsync(
context.Registration, context.TokenEndpoint, context.TokenRequest);
} }
Debug.Assert(context.TokenResponse is not null, SR.GetResourceString(SR.ID4007)); catch (ProtocolException exception)
if (!string.IsNullOrEmpty(context.TokenResponse.Error))
{ {
context.Reject( context.Reject(
error: context.TokenResponse.Error, error: exception.Error,
description: context.TokenResponse.ErrorDescription, description: exception.ErrorDescription,
uri: context.TokenResponse.ErrorUri); uri: exception.ErrorUri);
return default; return;
} }
return default;
} }
} }
@ -2073,7 +2047,7 @@ public static partial class OpenIddictClientHandlers
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireTokenRequest>() .AddFilter<RequireTokenRequest>()
.UseSingletonHandler<EvaluateValidatedBackchannelTokens>() .UseSingletonHandler<EvaluateValidatedBackchannelTokens>()
.SetOrder(ValidateTokenErrorParameters.Descriptor.Order + 1_000) .SetOrder(SendTokenRequest.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -3002,47 +2976,21 @@ public static partial class OpenIddictClientHandlers
// - application/json responses containing a JSON object listing the user claims as-is. // - application/json responses containing a JSON object listing the user claims as-is.
// - application/jwt responses containing a signed/encrypted JSON Web Token containing the user claims. // - application/jwt responses containing a signed/encrypted JSON Web Token containing the user claims.
(context.UserinfoResponse, (context.UserinfoTokenPrincipal, context.UserinfoToken)) = try
await _service.SendUserinfoRequestAsync(context.Registration, context.UserinfoEndpoint, context.UserinfoRequest);
}
}
/// <summary>
/// Contains the logic responsible for rejecting errored userinfo responses.
/// </summary>
public class ValidateUserinfoErrorParameters : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireUserinfoRequest>()
.UseSingletonHandler<ValidateUserinfoErrorParameters>()
.SetOrder(SendUserinfoRequest.Descriptor.Order + 1_000)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context)
{
if (context is null)
{ {
throw new ArgumentNullException(nameof(context)); (context.UserinfoResponse, (context.UserinfoTokenPrincipal, context.UserinfoToken)) =
await _service.SendUserinfoRequestAsync(context.Registration, context.UserinfoEndpoint, context.UserinfoRequest);
} }
Debug.Assert(context.UserinfoResponse is not null, SR.GetResourceString(SR.ID4007)); catch (ProtocolException exception)
if (!string.IsNullOrEmpty(context.UserinfoResponse.Error))
{ {
context.Reject( context.Reject(
error: context.UserinfoResponse.Error, error: exception.Error,
description: context.UserinfoResponse.ErrorDescription, description: exception.ErrorDescription,
uri: context.UserinfoResponse.ErrorUri); uri: exception.ErrorUri);
return default; return;
} }
return default;
} }
} }
@ -3058,7 +3006,7 @@ public static partial class OpenIddictClientHandlers
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireUserinfoRequest>() .AddFilter<RequireUserinfoRequest>()
.UseSingletonHandler<EvaluateValidatedUserinfoToken>() .UseSingletonHandler<EvaluateValidatedUserinfoToken>()
.SetOrder(ValidateUserinfoErrorParameters.Descriptor.Order + 1_000) .SetOrder(SendUserinfoRequest.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -4979,43 +4927,6 @@ public static partial class OpenIddictClientHandlers
} }
} }
/// <summary>
/// Contains the logic responsible for extracting potential errors from the response.
/// </summary>
public class HandleErrorResponse<TContext> : IOpenIddictClientHandler<TContext> where TContext : BaseValidatingContext
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>()
.UseSingletonHandler<HandleErrorResponse<TContext>>()
.SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (!string.IsNullOrEmpty(context.Transaction.Response?.Error))
{
context.Reject(
error: context.Transaction.Response.Error,
description: context.Transaction.Response.ErrorDescription,
uri: context.Transaction.Response.ErrorUri);
return default;
}
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible for attaching the appropriate parameters to the error response. /// Contains the logic responsible for attaching the appropriate parameters to the error response.
/// </summary> /// </summary>

107
src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs

@ -6,6 +6,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Validation; namespace OpenIddict.Validation;
@ -18,8 +19,8 @@ public static partial class OpenIddictValidationHandlers
/* /*
* Configuration response handling: * Configuration response handling:
*/ */
HandleErrorResponse<HandleConfigurationResponseContext>.Descriptor,
ValidateWellKnownConfigurationParameters.Descriptor, ValidateWellKnownConfigurationParameters.Descriptor,
HandleConfigurationErrorResponse.Descriptor,
ValidateIssuer.Descriptor, ValidateIssuer.Descriptor,
ExtractCryptographyEndpoint.Descriptor, ExtractCryptographyEndpoint.Descriptor,
ExtractIntrospectionEndpoint.Descriptor, ExtractIntrospectionEndpoint.Descriptor,
@ -28,8 +29,8 @@ public static partial class OpenIddictValidationHandlers
/* /*
* Cryptography response handling: * Cryptography response handling:
*/ */
HandleErrorResponse<HandleCryptographyResponseContext>.Descriptor,
ValidateWellKnownCryptographyParameters.Descriptor, ValidateWellKnownCryptographyParameters.Descriptor,
HandleCryptographyErrorResponse.Descriptor,
ExtractSigningKeys.Descriptor); ExtractSigningKeys.Descriptor);
/// <summary> /// <summary>
@ -43,7 +44,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>()
.UseSingletonHandler<ValidateWellKnownConfigurationParameters>() .UseSingletonHandler<ValidateWellKnownConfigurationParameters>()
.SetOrder(HandleErrorResponse<HandleConfigurationResponseContext>.Descriptor.Order + 1_000) .SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn) .SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build(); .Build();
@ -79,6 +80,10 @@ public static partial class OpenIddictValidationHandlers
// JsonElement instance using the same value type as the original parameter value. // JsonElement instance using the same value type as the original parameter value.
static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{ {
// Error parameters MUST be formatted as unique strings:
Parameters.Error or Parameters.ErrorDescription or Parameters.ErrorUri
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
// The following parameters MUST be formatted as unique strings: // The following parameters MUST be formatted as unique strings:
Metadata.IntrospectionEndpoint or Metadata.IntrospectionEndpoint or
Metadata.Issuer Metadata.Issuer
@ -108,6 +113,49 @@ public static partial class OpenIddictValidationHandlers
} }
} }
/// <summary>
/// Contains the logic responsible for surfacing potential errors from the configuration response.
/// </summary>
public class HandleConfigurationErrorResponse : IOpenIddictValidationHandler<HandleConfigurationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>()
.UseSingletonHandler<HandleConfigurationErrorResponse>()
.SetOrder(ValidateWellKnownConfigurationParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleConfigurationResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// Note: the specification doesn't define a standard way to return an error other than
// returning a 4xx status code. That said, some implementations are known to return
// JSON payloads similar to standard errored token responses. For more information, see
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse.
if (!string.IsNullOrEmpty(context.Response.Error))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6203), context.Response);
context.Reject(
error: Errors.ServerError,
description: SR.GetResourceString(SR.ID2144),
uri: SR.FormatID8000(SR.ID2144));
return default;
}
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible for extracting the issuer from the discovery document. /// Contains the logic responsible for extracting the issuer from the discovery document.
/// </summary> /// </summary>
@ -119,7 +167,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>()
.UseSingletonHandler<ValidateIssuer>() .UseSingletonHandler<ValidateIssuer>()
.SetOrder(ValidateWellKnownConfigurationParameters.Descriptor.Order + 1_000) .SetOrder(HandleConfigurationErrorResponse.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn) .SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build(); .Build();
@ -320,7 +368,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>()
.UseSingletonHandler<ValidateWellKnownCryptographyParameters>() .UseSingletonHandler<ValidateWellKnownCryptographyParameters>()
.SetOrder(HandleErrorResponse<HandleCryptographyResponseContext>.Descriptor.Order + 1_000) .SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn) .SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build(); .Build();
@ -356,6 +404,10 @@ public static partial class OpenIddictValidationHandlers
// JsonElement instance using the same value type as the original parameter value. // JsonElement instance using the same value type as the original parameter value.
static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{ {
// Error parameters MUST be formatted as unique strings:
Parameters.Error or Parameters.ErrorDescription or Parameters.ErrorUri
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
// The following parameters MUST be formatted as arrays of objects: // The following parameters MUST be formatted as arrays of objects:
JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element && JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element &&
element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element), element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element),
@ -379,6 +431,49 @@ public static partial class OpenIddictValidationHandlers
} }
} }
/// <summary>
/// Contains the logic responsible for surfacing potential errors from the cryptography response.
/// </summary>
public class HandleCryptographyErrorResponse : IOpenIddictValidationHandler<HandleCryptographyResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>()
.UseSingletonHandler<HandleCryptographyErrorResponse>()
.SetOrder(ValidateWellKnownCryptographyParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleCryptographyResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// Note: the specification doesn't define a standard way to return an error other than
// returning a 4xx status code. That said, some implementations are known to return
// JSON payloads similar to standard errored token responses. For more information, see
// https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse.
if (!string.IsNullOrEmpty(context.Response.Error))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6204), context.Response);
context.Reject(
error: Errors.ServerError,
description: SR.GetResourceString(SR.ID2145),
uri: SR.FormatID8000(SR.ID2145));
return default;
}
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible for extracting the signing keys from the JWKS document. /// Contains the logic responsible for extracting the signing keys from the JWKS document.
/// </summary> /// </summary>
@ -390,7 +485,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>()
.UseSingletonHandler<ExtractSigningKeys>() .UseSingletonHandler<ExtractSigningKeys>()
.SetOrder(ValidateWellKnownCryptographyParameters.Descriptor.Order + 1_000) .SetOrder(HandleCryptographyErrorResponse.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn) .SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build(); .Build();

67
src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs

@ -7,6 +7,7 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Security.Claims; using System.Security.Claims;
using System.Text.Json; using System.Text.Json;
using Microsoft.Extensions.Logging;
namespace OpenIddict.Validation; namespace OpenIddict.Validation;
@ -24,8 +25,8 @@ public static partial class OpenIddictValidationHandlers
/* /*
* Introspection response handling: * Introspection response handling:
*/ */
HandleErrorResponse<HandleIntrospectionResponseContext>.Descriptor,
ValidateWellKnownParameters.Descriptor, ValidateWellKnownParameters.Descriptor,
HandleErrorResponse.Descriptor,
HandleInactiveResponse.Descriptor, HandleInactiveResponse.Descriptor,
ValidateIssuer.Descriptor, ValidateIssuer.Descriptor,
ValidateTokenUsage.Descriptor, ValidateTokenUsage.Descriptor,
@ -102,7 +103,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>()
.UseSingletonHandler<ValidateWellKnownParameters>() .UseSingletonHandler<ValidateWellKnownParameters>()
.SetOrder(HandleErrorResponse<HandleIntrospectionResponseContext>.Descriptor.Order + 1_000) .SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn) .SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build(); .Build();
@ -138,25 +139,29 @@ public static partial class OpenIddictValidationHandlers
// JsonElement instance using the same value type as the original parameter value. // JsonElement instance using the same value type as the original parameter value.
static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{ {
// The following parameters MUST be formatted as booleans: // Error parameters MUST be formatted as unique strings:
Parameters.Error or Parameters.ErrorDescription or Parameters.ErrorUri
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
// The following claims MUST be formatted as booleans:
Claims.Active => ((JsonElement) value).ValueKind is JsonValueKind.True or JsonValueKind.False, Claims.Active => ((JsonElement) value).ValueKind is JsonValueKind.True or JsonValueKind.False,
// The following parameters MUST be formatted as unique strings: // The following claims MUST be formatted as unique strings:
Claims.JwtId or Claims.Issuer or Claims.Scope or Claims.TokenUsage Claims.JwtId or Claims.Issuer or Claims.Scope or Claims.TokenUsage
=> ((JsonElement) value).ValueKind is JsonValueKind.String, => ((JsonElement) value).ValueKind is JsonValueKind.String,
// The following parameters MUST be formatted as strings or arrays of strings: // The following claims MUST be formatted as strings or arrays of strings:
// //
// Note: empty arrays and arrays that contain a single value are also considered valid. // Note: empty arrays and arrays that contain a single value are also considered valid.
Claims.Audience => ((JsonElement) value) is JsonElement element && Claims.Audience => ((JsonElement) value) is JsonElement element &&
element.ValueKind is JsonValueKind.String || element.ValueKind is JsonValueKind.String ||
(element.ValueKind is JsonValueKind.Array && ValidateStringArray(element)), (element.ValueKind is JsonValueKind.Array && ValidateStringArray(element)),
// The following parameters MUST be formatted as numeric dates: // The following claims MUST be formatted as numeric dates:
Claims.ExpiresAt or Claims.IssuedAt or Claims.NotBefore Claims.ExpiresAt or Claims.IssuedAt or Claims.NotBefore
=> ((JsonElement) value).ValueKind is JsonValueKind.Number, => ((JsonElement) value).ValueKind is JsonValueKind.Number,
// Parameters that are not in the well-known list can be of any type. // Claims that are not in the well-known list can be of any type.
_ => true _ => true
}; };
@ -175,6 +180,52 @@ public static partial class OpenIddictValidationHandlers
} }
} }
/// <summary>
/// Contains the logic responsible for surfacing potential errors from the introspection response.
/// </summary>
public class HandleErrorResponse : IOpenIddictValidationHandler<HandleIntrospectionResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>()
.UseSingletonHandler<HandleErrorResponse>()
.SetOrder(ValidateWellKnownParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleIntrospectionResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// Note: the specification requires returning most errors (e.g invalid token errors)
// as "active: false" responses instead of as proper OAuth 2.0 error responses.
// For more information, see https://datatracker.ietf.org/doc/html/rfc7662#section-2.3.
if (!string.IsNullOrEmpty(context.Response.Error))
{
context.Logger.LogInformation(SR.GetResourceString(SR.ID6205), context.Response);
context.Reject(
error: context.Response.Error switch
{
Errors.UnauthorizedClient => Errors.UnauthorizedClient,
_ => Errors.ServerError
},
description: SR.GetResourceString(SR.ID2146),
uri: SR.FormatID8000(SR.ID2146));
return default;
}
return default;
}
}
/// <summary> /// <summary>
/// Contains the logic responsible for extracting the active: false marker from the response. /// Contains the logic responsible for extracting the active: false marker from the response.
/// </summary> /// </summary>
@ -186,7 +237,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>() = OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>()
.UseSingletonHandler<HandleInactiveResponse>() .UseSingletonHandler<HandleInactiveResponse>()
.SetOrder(ValidateWellKnownParameters.Descriptor.Order + 1_000) .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn) .SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build(); .Build();

9
src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs

@ -10,6 +10,7 @@ using System.Globalization;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Abstractions.OpenIddictExceptions;
namespace OpenIddict.Validation; namespace OpenIddict.Validation;
@ -336,14 +337,14 @@ public static partial class OpenIddictValidationHandlers
}) ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0141)); }) ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0141));
} }
catch (Exception exception) catch (ProtocolException exception)
{ {
context.Logger.LogDebug(exception, SR.GetResourceString(SR.ID6155)); context.Logger.LogDebug(exception, SR.GetResourceString(SR.ID6155));
context.Reject( context.Reject(
error: Errors.InvalidToken, error: exception.Error,
description: SR.GetResourceString(SR.ID2004), description: exception.ErrorDescription,
uri: SR.FormatID8000(SR.ID2004)); uri: exception.ErrorUri);
return; return;
} }

37
src/OpenIddict.Validation/OpenIddictValidationHandlers.cs

@ -365,41 +365,4 @@ public static partial class OpenIddictValidationHandlers
return default; return default;
} }
} }
/// <summary>
/// Contains the logic responsible for extracting potential errors from the response.
/// </summary>
public class HandleErrorResponse<TContext> : IOpenIddictValidationHandler<TContext> where TContext : BaseValidatingContext
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<TContext>()
.UseSingletonHandler<HandleErrorResponse<TContext>>()
.SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (!string.IsNullOrEmpty(context.Transaction.Response?.Error))
{
context.Reject(
error: context.Transaction.Response.Error,
description: context.Transaction.Response.ErrorDescription,
uri: context.Transaction.Response.ErrorUri);
return default;
}
return default;
}
}
} }

Loading…
Cancel
Save