diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx
index b4f4ae4b..826d194a 100644
--- a/src/OpenIddict.Abstractions/OpenIddictResources.resx
+++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx
@@ -1524,7 +1524,7 @@ Reference the 'OpenIddict.Client.SystemNetHttp' package and call 'services.AddOp
The token was rejected by the remote authentication server.
- The '{0}' claim is malformed or isn't of the expected type.
+ The '{0}' parameter is malformed or isn't of the expected type.
An introspection response containing a malformed issuer was returned.
diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs
index 5ca81d9d..59693226 100644
--- a/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs
+++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Discovery.cs
@@ -5,6 +5,7 @@
*/
using System.Collections.Immutable;
+using System.Text.Json;
using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Client;
@@ -18,6 +19,7 @@ public static partial class OpenIddictClientHandlers
* Configuration response handling:
*/
HandleErrorResponse.Descriptor,
+ ValidateWellKnownConfigurationParameters.Descriptor,
ValidateIssuer.Descriptor,
ExtractAuthorizationEndpoint.Descriptor,
ExtractCryptographyEndpoint.Descriptor,
@@ -35,8 +37,92 @@ public static partial class OpenIddictClientHandlers
* Cryptography response handling:
*/
HandleErrorResponse.Descriptor,
+ ValidateWellKnownCryptographyParameters.Descriptor,
ExtractSigningKeys.Descriptor);
+ ///
+ /// Contains the logic responsible for validating the well-known parameters contained in the configuration response.
+ ///
+ public class ValidateWellKnownConfigurationParameters : IOpenIddictClientHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictClientHandlerDescriptor Descriptor { get; }
+ = OpenIddictClientHandlerDescriptor.CreateBuilder()
+ .UseSingletonHandler()
+ .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
+ .SetType(OpenIddictClientHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(HandleConfigurationResponseContext context!!)
+ {
+ foreach (var parameter in context.Response.GetParameters())
+ {
+ if (!ValidateParameterType(parameter.Key, parameter.Value))
+ {
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.FormatID2107(parameter.Key),
+ uri: SR.FormatID8000(SR.ID2107));
+
+ return default;
+ }
+ }
+
+ return default;
+
+ // Note: in the typical case, the response parameters should be deserialized from a
+ // JSON response and thus natively stored as System.Text.Json.JsonElement instances.
+ //
+ // In the rare cases where the underlying value wouldn't be a JsonElement instance
+ // (e.g when custom parameters are manually added to the response), the static
+ // conversion operator would take care of converting the underlying value to a
+ // JsonElement instance using the same value type as the original parameter value.
+ static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
+ {
+ // The following parameters MUST be formatted as unique strings:
+ Metadata.AuthorizationEndpoint or
+ Metadata.Issuer or
+ Metadata.JwksUri or
+ Metadata.TokenEndpoint or
+ Metadata.UserinfoEndpoint
+ => ((JsonElement) value).ValueKind is JsonValueKind.String,
+
+ // The following parameters MUST be formatted as arrays of strings:
+ Metadata.CodeChallengeMethodsSupported or
+ Metadata.GrantTypesSupported or
+ Metadata.ResponseModesSupported or
+ Metadata.ResponseTypesSupported or
+ Metadata.ScopesSupported or
+ Metadata.TokenEndpointAuthMethodsSupported
+ => ((JsonElement) value) is JsonElement element &&
+ element.ValueKind is JsonValueKind.Array && ValidateStringArray(element),
+
+ // The following parameters MUST be formatted as booleans:
+ Metadata.AuthorizationResponseIssParameterSupported
+ => ((JsonElement) value).ValueKind is JsonValueKind.True or JsonValueKind.False,
+
+ // Parameters that are not in the well-known list can be of any type.
+ _ => true
+ };
+
+ static bool ValidateStringArray(JsonElement element)
+ {
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind is not JsonValueKind.String)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+
///
/// Contains the logic responsible for extracting the issuer from the discovery document.
///
@@ -48,7 +134,7 @@ public static partial class OpenIddictClientHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
+ .SetOrder(ValidateWellKnownConfigurationParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
@@ -503,6 +589,71 @@ public static partial class OpenIddictClientHandlers
}
}
+ ///
+ /// Contains the logic responsible for validating the well-known parameters contained in the JWKS response.
+ ///
+ public class ValidateWellKnownCryptographyParameters : IOpenIddictClientHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictClientHandlerDescriptor Descriptor { get; }
+ = OpenIddictClientHandlerDescriptor.CreateBuilder()
+ .UseSingletonHandler()
+ .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
+ .SetType(OpenIddictClientHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(HandleCryptographyResponseContext context!!)
+ {
+ foreach (var parameter in context.Response.GetParameters())
+ {
+ if (!ValidateParameterType(parameter.Key, parameter.Value))
+ {
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.FormatID2107(parameter.Key),
+ uri: SR.FormatID8000(SR.ID2107));
+
+ return default;
+ }
+ }
+
+ return default;
+
+ // Note: in the typical case, the response parameters should be deserialized from a
+ // JSON response and thus natively stored as System.Text.Json.JsonElement instances.
+ //
+ // In the rare cases where the underlying value wouldn't be a JsonElement instance
+ // (e.g when custom parameters are manually added to the response), the static
+ // conversion operator would take care of converting the underlying value to a
+ // JsonElement instance using the same value type as the original parameter value.
+ static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
+ {
+ // The following parameters MUST be formatted as arrays of objects:
+ JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element &&
+ element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element),
+
+ // Parameters that are not in the well-known list can be of any type.
+ _ => true
+ };
+
+ static bool ValidateObjectArray(JsonElement element)
+ {
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind is not JsonValueKind.Object)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+
///
/// Contains the logic responsible for extracting the signing keys from the JWKS document.
///
@@ -514,7 +665,7 @@ public static partial class OpenIddictClientHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
+ .SetOrder(ValidateWellKnownCryptographyParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Exchange.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Exchange.cs
index dddceb7d..4393df4c 100644
--- a/src/OpenIddict.Client/OpenIddictClientHandlers.Exchange.cs
+++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Exchange.cs
@@ -40,17 +40,15 @@ public static partial class OpenIddictClientHandlers
{
foreach (var parameter in context.Response.GetParameters())
{
- if (ValidateParameterType(parameter.Key, parameter.Value))
+ if (!ValidateParameterType(parameter.Key, parameter.Value))
{
- continue;
- }
-
- context.Reject(
- error: Errors.ServerError,
- description: SR.FormatID2107(parameter.Key),
- uri: SR.FormatID8000(SR.ID2107));
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.FormatID2107(parameter.Key),
+ uri: SR.FormatID8000(SR.ID2107));
- return default;
+ return default;
+ }
}
return default;
@@ -64,11 +62,11 @@ public static partial class OpenIddictClientHandlers
// JsonElement instance using the same value type as the original parameter value.
static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{
- // The 'access_token', 'id_token' and 'refresh_token' parameters MUST be formatted as unique strings.
+ // The following parameters MUST be formatted as unique strings:
Parameters.AccessToken or Parameters.IdToken or Parameters.RefreshToken
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
- // The 'expires_in' parameter MUST be formatted as a numeric date value.
+ // The following parameters MUST be formatted as numeric dates:
Parameters.ExpiresIn => ((JsonElement) value).ValueKind is JsonValueKind.Number,
// Parameters that are not in the well-known list can be of any type.
diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.Userinfo.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.Userinfo.cs
index 19f8e9cc..fb5005ce 100644
--- a/src/OpenIddict.Client/OpenIddictClientHandlers.Userinfo.cs
+++ b/src/OpenIddict.Client/OpenIddictClientHandlers.Userinfo.cs
@@ -49,17 +49,15 @@ public static partial class OpenIddictClientHandlers
foreach (var parameter in context.Response.GetParameters())
{
- if (ValidateClaimType(parameter.Key, parameter.Value))
+ if (!ValidateParameterType(parameter.Key, parameter.Value))
{
- continue;
- }
-
- context.Reject(
- error: Errors.ServerError,
- description: SR.FormatID2107(parameter.Key),
- uri: SR.FormatID8000(SR.ID2107));
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.FormatID2107(parameter.Key),
+ uri: SR.FormatID8000(SR.ID2107));
- return default;
+ return default;
+ }
}
return default;
@@ -71,9 +69,9 @@ public static partial class OpenIddictClientHandlers
// (e.g when custom parameters are manually added to the response), the static
// conversion operator would take care of converting the underlying value to a
// JsonElement instance using the same value type as the original parameter value.
- static bool ValidateClaimType(string name, OpenIddictParameter value) => name switch
+ static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{
- // The 'sub' parameter MUST be formatted as a unique string value.
+ // The following parameters MUST be formatted as unique strings:
Claims.Subject => ((JsonElement) value).ValueKind is JsonValueKind.String,
// Parameters that are not in the well-known list can be of any type.
diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs
index df8f2940..e291a0ec 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Discovery.cs
@@ -5,6 +5,7 @@
*/
using System.Collections.Immutable;
+using System.Text.Json;
using Microsoft.IdentityModel.Tokens;
namespace OpenIddict.Validation;
@@ -18,6 +19,7 @@ public static partial class OpenIddictValidationHandlers
* Configuration response handling:
*/
HandleErrorResponse.Descriptor,
+ ValidateWellKnownConfigurationParameters.Descriptor,
ValidateIssuer.Descriptor,
ExtractCryptographyEndpoint.Descriptor,
ExtractIntrospectionEndpoint.Descriptor,
@@ -27,8 +29,80 @@ public static partial class OpenIddictValidationHandlers
* Cryptography response handling:
*/
HandleErrorResponse.Descriptor,
+ ValidateWellKnownCryptographyParameters.Descriptor,
ExtractSigningKeys.Descriptor);
+ ///
+ /// Contains the logic responsible for validating the well-known parameters contained in the configuration response.
+ ///
+ public class ValidateWellKnownConfigurationParameters : IOpenIddictValidationHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ .UseSingletonHandler()
+ .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
+ .SetType(OpenIddictValidationHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(HandleConfigurationResponseContext context!!)
+ {
+ foreach (var parameter in context.Response.GetParameters())
+ {
+ if (!ValidateParameterType(parameter.Key, parameter.Value))
+ {
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.FormatID2107(parameter.Key),
+ uri: SR.FormatID8000(SR.ID2107));
+
+ return default;
+ }
+ }
+
+ return default;
+
+ // Note: in the typical case, the response parameters should be deserialized from a
+ // JSON response and thus natively stored as System.Text.Json.JsonElement instances.
+ //
+ // In the rare cases where the underlying value wouldn't be a JsonElement instance
+ // (e.g when custom parameters are manually added to the response), the static
+ // conversion operator would take care of converting the underlying value to a
+ // JsonElement instance using the same value type as the original parameter value.
+ static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
+ {
+ // The following parameters MUST be formatted as unique strings:
+ Metadata.IntrospectionEndpoint or
+ Metadata.Issuer
+ => ((JsonElement) value).ValueKind is JsonValueKind.String,
+
+ // The following parameters MUST be formatted as arrays of strings:
+ Metadata.IntrospectionEndpointAuthMethodsSupported
+ => ((JsonElement) value) is JsonElement element &&
+ element.ValueKind is JsonValueKind.Array && ValidateStringArray(element),
+
+ // Parameters that are not in the well-known list can be of any type.
+ _ => true
+ };
+
+ static bool ValidateStringArray(JsonElement element)
+ {
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind is not JsonValueKind.String)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+
///
/// Contains the logic responsible for extracting the issuer from the discovery document.
///
@@ -40,7 +114,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
+ .SetOrder(ValidateWellKnownConfigurationParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
@@ -199,6 +273,71 @@ public static partial class OpenIddictValidationHandlers
}
}
+ ///
+ /// Contains the logic responsible for validating the well-known parameters contained in the JWKS response.
+ ///
+ public class ValidateWellKnownCryptographyParameters : IOpenIddictValidationHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ .UseSingletonHandler()
+ .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
+ .SetType(OpenIddictValidationHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(HandleCryptographyResponseContext context!!)
+ {
+ foreach (var parameter in context.Response.GetParameters())
+ {
+ if (!ValidateParameterType(parameter.Key, parameter.Value))
+ {
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.FormatID2107(parameter.Key),
+ uri: SR.FormatID8000(SR.ID2107));
+
+ return default;
+ }
+ }
+
+ return default;
+
+ // Note: in the typical case, the response parameters should be deserialized from a
+ // JSON response and thus natively stored as System.Text.Json.JsonElement instances.
+ //
+ // In the rare cases where the underlying value wouldn't be a JsonElement instance
+ // (e.g when custom parameters are manually added to the response), the static
+ // conversion operator would take care of converting the underlying value to a
+ // JsonElement instance using the same value type as the original parameter value.
+ static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
+ {
+ // The following parameters MUST be formatted as arrays of objects:
+ JsonWebKeySetParameterNames.Keys => ((JsonElement) value) is JsonElement element &&
+ element.ValueKind is JsonValueKind.Array && ValidateObjectArray(element),
+
+ // Parameters that are not in the well-known list can be of any type.
+ _ => true
+ };
+
+ static bool ValidateObjectArray(JsonElement element)
+ {
+ foreach (var item in element.EnumerateArray())
+ {
+ if (item.ValueKind is not JsonValueKind.Object)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+
///
/// Contains the logic responsible for extracting the signing keys from the JWKS document.
///
@@ -210,7 +349,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
+ .SetOrder(ValidateWellKnownCryptographyParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs
index 4fcdd479..e49de34e 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.Introspection.cs
@@ -26,8 +26,8 @@ public static partial class OpenIddictValidationHandlers
* Introspection response handling:
*/
HandleErrorResponse.Descriptor,
+ ValidateWellKnownParameters.Descriptor,
HandleInactiveResponse.Descriptor,
- ValidateWellKnownClaims.Descriptor,
ValidateIssuer.Descriptor,
ValidateTokenUsage.Descriptor,
PopulateClaims.Descriptor);
@@ -83,85 +83,34 @@ public static partial class OpenIddictValidationHandlers
}
///
- /// Contains the logic responsible for extracting the active: false marker from the response.
+ /// Contains the logic responsible for validating the well-known parameters contained in the introspection response.
///
- public class HandleInactiveResponse : IOpenIddictValidationHandler
+ public class ValidateWellKnownParameters : IOpenIddictValidationHandler
{
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder()
- .UseSingletonHandler()
+ .UseSingletonHandler()
.SetOrder(HandleErrorResponse.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
- ///
- public ValueTask HandleAsync(HandleIntrospectionResponseContext context!!)
- {
- // Note: the introspection specification requires that server return "active: false" instead of a proper
- // OAuth 2.0 error when the token is invalid, expired, revoked or invalid for any other reason.
- // While OpenIddict's server can be tweaked to return a proper error (by removing NormalizeErrorResponse)
- // from the enabled handlers, supporting "active: false" is required to ensure total compatibility.
-
- if (!context.Response.TryGetParameter(Parameters.Active, out var parameter))
- {
- context.Reject(
- error: Errors.ServerError,
- description: SR.FormatID2105(Parameters.Active),
- uri: SR.FormatID8000(SR.ID2105));
-
- return default;
- }
-
- // Note: if the parameter cannot be converted to a boolean instance, the default value
- // (false) is returned by the static operator, which is appropriate for this check.
- if (!(bool) parameter)
- {
- context.Reject(
- error: Errors.InvalidToken,
- description: SR.GetResourceString(SR.ID2106),
- uri: SR.FormatID8000(SR.ID2106));
-
- return default;
- }
-
- return default;
- }
- }
-
- ///
- /// Contains the logic responsible for validating the well-known claims contained in the introspection response.
- ///
- public class ValidateWellKnownClaims : IOpenIddictValidationHandler
- {
- ///
- /// Gets the default descriptor definition assigned to this handler.
- ///
- public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
- = OpenIddictValidationHandlerDescriptor.CreateBuilder()
- .UseSingletonHandler()
- .SetOrder(HandleInactiveResponse.Descriptor.Order + 1_000)
- .SetType(OpenIddictValidationHandlerType.BuiltIn)
- .Build();
-
///
public ValueTask HandleAsync(HandleIntrospectionResponseContext context!!)
{
foreach (var parameter in context.Response.GetParameters())
{
- if (ValidateClaimType(parameter.Key, parameter.Value))
+ if (!ValidateParameterType(parameter.Key, parameter.Value))
{
- continue;
- }
-
- context.Reject(
- error: Errors.ServerError,
- description: SR.FormatID2107(parameter.Key),
- uri: SR.FormatID8000(SR.ID2107));
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.FormatID2107(parameter.Key),
+ uri: SR.FormatID8000(SR.ID2107));
- return default;
+ return default;
+ }
}
return default;
@@ -173,24 +122,27 @@ public static partial class OpenIddictValidationHandlers
// (e.g when custom parameters are manually added to the response), the static
// conversion operator would take care of converting the underlying value to a
// JsonElement instance using the same value type as the original parameter value.
- static bool ValidateClaimType(string name, OpenIddictParameter value) => name switch
+ static bool ValidateParameterType(string name, OpenIddictParameter value) => name switch
{
- // The 'jti', 'iss', 'scope' and 'token_usage' claims MUST be formatted as a unique string.
+ // The following parameters MUST be formatted as booleans:
+ Claims.Active => ((JsonElement) value).ValueKind is JsonValueKind.True or JsonValueKind.False,
+
+ // The following parameters MUST be formatted as unique strings:
Claims.JwtId or Claims.Issuer or Claims.Scope or Claims.TokenUsage
=> ((JsonElement) value).ValueKind is JsonValueKind.String,
- // The 'aud' claim MUST be represented either as a unique string or as an array of strings.
+ // The following parameters MUST be formatted as strings or arrays of strings:
//
// Note: empty arrays and arrays that contain a single value are also considered valid.
Claims.Audience => ((JsonElement) value) is JsonElement element &&
element.ValueKind is JsonValueKind.String ||
(element.ValueKind is JsonValueKind.Array && ValidateStringArray(element)),
- // The 'exp', 'iat' and 'nbf' claims MUST be formatted as numeric date values.
+ // The following parameters MUST be formatted as numeric dates:
Claims.ExpiresAt or Claims.IssuedAt or Claims.NotBefore
=> ((JsonElement) value).ValueKind is JsonValueKind.Number,
- // Claims that are not in the well-known list can be of any type.
+ // Parameters that are not in the well-known list can be of any type.
_ => true
};
@@ -209,6 +161,54 @@ public static partial class OpenIddictValidationHandlers
}
}
+ ///
+ /// Contains the logic responsible for extracting the active: false marker from the response.
+ ///
+ public class HandleInactiveResponse : IOpenIddictValidationHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ .UseSingletonHandler()
+ .SetOrder(ValidateWellKnownParameters.Descriptor.Order + 1_000)
+ .SetType(OpenIddictValidationHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(HandleIntrospectionResponseContext context!!)
+ {
+ // Note: the introspection specification requires that server return "active: false" instead of a proper
+ // OAuth 2.0 error when the token is invalid, expired, revoked or invalid for any other reason.
+ // While OpenIddict's server can be tweaked to return a proper error (by removing NormalizeErrorResponse)
+ // from the enabled handlers, supporting "active: false" is required to ensure total compatibility.
+
+ var active = (bool?) context.Response[Parameters.Active];
+ if (active is null)
+ {
+ context.Reject(
+ error: Errors.ServerError,
+ description: SR.FormatID2105(Parameters.Active),
+ uri: SR.FormatID8000(SR.ID2105));
+
+ return default;
+ }
+
+ if (active is not true)
+ {
+ context.Reject(
+ error: Errors.InvalidToken,
+ description: SR.GetResourceString(SR.ID2106),
+ uri: SR.FormatID8000(SR.ID2106));
+
+ return default;
+ }
+
+ return default;
+ }
+ }
+
///
/// Contains the logic responsible for extracting the issuer from the introspection response.
///
@@ -220,7 +220,7 @@ public static partial class OpenIddictValidationHandlers
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder()
.UseSingletonHandler()
- .SetOrder(ValidateWellKnownClaims.Descriptor.Order + 1_000)
+ .SetOrder(ValidateWellKnownParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();