From 10195fd483043f6a36b4027a5de94d498623c4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sun, 6 Sep 2026 14:59:38 +0200 Subject: [PATCH] Allow client assertion audiences to be represented as JSON arrays --- .../OpenIddictServerHandlers.cs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index ea2e5583..29b7c66f 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -780,7 +780,7 @@ public static partial class OpenIddictServerHandlers // For more information, see // https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication and // https://datatracker.ietf.org/doc/html/rfc7523#section-3. - if (context.ClientAssertionPrincipal.GetAudiences() is not [_]) + if (context.ClientAssertionPrincipal.GetAudiences() is not [{ Length: > 0 }]) { context.Reject( error: Errors.InvalidRequest, @@ -809,15 +809,21 @@ public static partial class OpenIddictServerHandlers static bool ValidateClaimGroup(string name, List values) => name switch { - // The following claims MUST be represented as unique strings. - // - // Important: client assertions with multiple audiences was initially deliberately supported by - // the OpenID Connect and Assertion Framework for OAuth 2.0 Client Authentication specifications. - // Since 2025, using multiple audiences is no longer allowed for security reasons. As such, the - // "aud" claim present in client assertions MUST always be represented as a single string. + // The following claims MUST be represented as unique strings or array of strings. // - // See https://www.ietf.org/archive/id/draft-ietf-oauth-rfc7523bis-01.html#section-4 for more information. - Claims.Audience or Claims.AuthorizedParty or Claims.Issuer or Claims.JwtId or Claims.Subject + // Note: the initial version of the "Updates to Audience Values for OAuth 2.0 Authorization Servers" + // specification initially required that the "aud" claim be represented as a unique string but more + // recent versions of the specification allow the "aud" claim to be represented as a JSON array of strings. + Claims.Audience => values.TrueForAll(static value => value.ValueType is ClaimValueTypes.String) || + // Note: a unique claim using the special JSON_ARRAY claim value type is allowed + // if the individual elements of the parsed JSON array are all string values. + (values is [{ ValueType: JsonClaimValueTypes.JsonArray, Value: string value }] && + JsonSerializer.Deserialize(value, OpenIddictSerializer.Default.JsonElement) + is { ValueKind: JsonValueKind.Array } element && + OpenIddictHelpers.ValidateArrayElements(element, JsonValueKind.String)), + + // The following claims MUST be represented as unique strings. + Claims.AuthorizedParty or Claims.Issuer or Claims.JwtId or Claims.Subject => values is [{ ValueType: ClaimValueTypes.String }], // The following claims MUST be represented as unique numeric dates.