Skip to content

DefaultIDTokenValidator rejects array form of aud claim (RFC 7519 §4.1.3) #281

Description

@RicardoKoch

Describe the bug?

DefaultIDTokenValidator rejects ID tokens whose aud claim is a JSON
array, even when clientId is one of the array's members. RFC 7519
§4.1.3 explicitly permits aud to be either a scalar string or an
array of strings, and OIDC Core 1.0 §3.1.3.7 requires only that the
client be listed as a valid audience — not that the claim be a scalar.

This causes ID-token validation to fail against authorization servers
that emit array-form aud (federation scenarios, custom AS
configurations, third-party IdPs proxied through Okta).

What is expected to happen?

When aud is ["clientId", ...] and clientId is registered for the
client, validation should succeed.

What is the actual behavior?

Validation throws JWTError.invalidAudience and the sign-in fails.

The relevant code in
Sources/AuthFoundation/JWT/Internal/DefaultIDTokenValidator.swift:

case .audience:
    guard token[.audience] == clientId
    else {
        throw JWTError.invalidAudience
    }

token[.audience] returns String?, which silently maps the
array-form claim to nil and then fails the equality check.

### Reproduction Steps?

1. Configure an OAuth2Client with clientId = "client_id_X".
2. Have the IdP issue an ID token whose payload contains
"aud": ["client_id_X", "another_audience"].
3. Trigger ID-token validation (e.g. via BrowserSignin.signIn or any
path that runs DefaultIDTokenValidator).
4. Observe JWTError.invalidAudience.

A unit-test reproduction is included in PR #280
(testAudienceArrayContainingClientIdSucceeds /
testAudienceArrayNotContainingClientIdFails).

### Additional Information?

Fix proposed in PR #280 — accept the array form when clientId is a
member, fall back to the existing scalar comparison otherwise.

References:
- RFC 7519 §4.1.3 — https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3
- OIDC Core 1.0 §3.1.3.7 — https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation

### SDK Version(s)

2.1.5 (also reproducible on 1.8.2; the validator code is structurally
identical between the two versions).

### Build Information

Not build-related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions