8000 FAPI 2.0: reject array-valued aud in client assertions by Inf1mum · Pull Request #54783 · keycloak/keycloak · GitHub
[go: up one dir, main page]

FAPI 2.0: reject array-valued aud in client assertions - #54783

Open
Inf1mum wants to merge 3 commits into
keycloak:mainfrom
Inf1mum:fix/54743-fapi-aud-array
Open

Inf1mum wants to merge 3 commits into
keycloak:mainfrom
Inf1mum:fix/54743-fapi-aud-array

Conversation

@Inf1mum
@Inf1mum Inf1mum commented Nov 6, 2026
Copy link
Copy Markdown
Contributor

Closes #54743

What

With the FAPI 2.0 / OAuth 2.1 client profiles (secure-client-authentication-assertion executor), a client authentication assertion whose aud claim is a single-element JSON array containing the issuer (for example ["https://host/realms/myrealm"]) was accepted. FAPI 2.0 Security Profile (Final), §5.3.2.1 requires the authorization server to accept only its issuer identifier, as a string, in the aud claim of client authentication assertions.

How

SecureClientAuthenticationAssertionExecutor previously decoded the assertion into JsonWebToken and validated the audience through getAudience(). Because the aud field is annotated with @StringOrArray, a JSON string and a single-element array are indistinguishable after deserialization, which let ["<issuer>"] pass both the length != 1 and the hasAnyAudience checks.

The executor now reads the assertion payload as a JSON tree and requires the aud claim to be a textual node equal to the realm issuer. All the other cases keep their existing behavior: missing audience, multiple audiences and non-issuer audiences are still rejected with invalid audience in client assertion.

The change is scoped to the executor, which is only active in the fapi-2-dpop-security-profile, fapi-2-dpop-message-signing and oauth-2-1-* default profiles. The general (non-FAPI) private_key_jwt / client_secret_jwt client authentication flow is untouched and continues to accept the array form, so the semantics of the allow-multiple-audiences-for-jwt-client-authentication realm option are unaffected.

Testing

Added SecureClientAuthenticationAssertionExecutorTest covering:

  • aud = issuer as a string is accepted with the profile
  • aud = ["<issuer>"] (single-element array) is rejected with the profile (the case from the issue)
  • aud = ["<issuer>", "<other>"] is rejected with the profile
  • aud = token endpoint URL as a string is still rejected with the profile
  • aud = ["<issuer>"] is still accepted without the profile (general flow unchanged)

The integration tests run in CI.

Signed-off-by: Inf1mum <Inf1mum@users.noreply.github.com>
@Inf1mum
Inf1mum requested a review from a team as a code owner November 6, 2026 17:17
Copilot AI balanced review requested due to automatic review settings November 6, 2026 17:17
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Shared SSO cookies are not cleared between tests, so subsequent tests skip the login form and fail before exercising their audience assertions.

1 open issue
What changed in this PR

This PR addresses #54743 by requiring a string-valued issuer audience in client assertions when the secure assertion executor applies, while leaving general JWT client authentication unchanged.

Changes:

  • Validate the raw JSON audience type and issuer value instead of the normalized token audience.
  • Add integration tests for string and array audiences, including acceptance without the policy.
File Description
tests/​base/​src/​test/​java/​org/​keycloak/​tests/​client/​policies/​SecureClientAuthenticationAssertionExecutorTest.java Adds audience validation and non-policy compatibility tests.
services/​src/​main/​java/​org/​keycloak/​services/​clientpolicy/​executor/​SecureClientAuthenticationAssertionExecutor.java Requires aud to be a JSON string equal to the realm issuer.

Review effort: Balanced

…ecutorTest

Signed-off-by: Inf1mum <Inf1mum@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 6, 2026 20:53
@Inf1mum
Inf1mum commented Nov 6, 2026
Copy link
Copy Markdown
Contributor Author

changes pushed: fixed spotless import ordering in the new test

Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The teardown leaves a login session active in the reused realm and WebDriver, causing subsequent tests to follow SSO and fail on a missing login form before checking the audience.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity Clear the realm login session between test cases

tests/​base/​src/​test/​java/​org/​keycloak/​tests/​client/​policies/​SecureClientAuthenticationAssertionExecutorTest.java:73

The realm and WebDriver are reused across test methods, but this teardown leaves the user's login session active. After the first case, oauth.doLogin() follows SSO and then tries to fill a nonexistent login form, causing subsequent cases to fail before checking the audience; log out the realm's sessions here or register equivalent per-test cleanup.

Review effort: Balanced

…ecutorTest

Signed-off-by: Inf1mum <Inf1mum@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 6, 2026 23:58
Copilot AI left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are identified for the FAPI 2.0 client-assertion audience validation change.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@Inf1mum
Inf1mum commented Nov 7, 2026
Copy link
Copy Markdown
Contributor Author

CI failure analysis — infra flake in the Admin UI E2E suite, the change is not in the failure path:

  • Admin UI E2E (chromium) (run 42315284087) failed on test/user-federation/kerberos.spec.ts:144 › User Fed Kerberos tests › Edit Kerberos provider › Should save several allowed Kerberos realms — locator.click timed out after 30s; the same job also shows multipl BD4F e switch:* toggles failing on the first attempt with "Clicking the checkbox did not change its state".
  • The same Admin UI E2E (chromium) job passed on main a few hours later (run 42336876762, job 128296713397), and this PR's own Account UI E2E (chromium) job passed in the same run.
  • The PR changes server-side FAPI 2.0 client-assertion aud validation (Java) and does not touch the Admin UI; all Keycloak CI suites, CodeQL, Guides, Documentation and the DCO check are green.

A rerun of the workflow is not available to external contributors, so I am leaving the evidence here instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FAPI 2.0 client profile accepts a client assertion whose aud is a single-element array

2 participants

0