Skip to content

Enforce audience check for self-referencing OIDC identity providers - #4055

Merged
duanemay merged 3 commits into
developfrom
fix/self-oidc-audience-bypass
Aug 27, 2026
Merged

Enforce audience check for self-referencing OIDC identity providers#4055
duanemay merged 3 commits into
developfrom
fix/self-oidc-audience-bypass

Conversation

@duanemay

Copy link
Copy Markdown
Member

An IdP registered with an issuer equal to the current zone's own token
endpoint skipped audience validation on the presented id_token, since
that check was only wired up for external OIDC providers. Any UAA-signed
JWT for any client, not just the self-referencing IdP's own relying
party, was accepted as a valid id_token for that login, letting a token
issued in one client context be replayed to authenticate a different
external-OIDC-mapped identity.

Now the relying party's client ID is checked against the token's aud
claim whenever one is configured, matching the validation already done
for external providers. The internal id_token exchange used by the JWT
bearer grant is unaffected, since it never sets a relying party ID.

An IdP registered with an issuer equal to the current zone's own token
endpoint skipped audience validation on the presented id_token, since
that check was only wired up for external OIDC providers. Any UAA-signed
JWT for any client, not just the self-referencing IdP's own relying
party, was accepted as a valid id_token for that login, letting a token
issued in one client context be replayed to authenticate a different
external-OIDC-mapped identity.

Now the relying party's client ID is checked against the token's aud
claim whenever one is configured, matching the validation already done
for external providers. The internal id_token exchange used by the JWT
bearer grant is unaffected, since it never sets a relying party ID.
Copilot AI lite review requested due to automatic review settings August 25, 2026 20:26

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.

Pull request overview

This PR closes a security gap in the OIDC external authentication flow where a self-referencing OIDC IdP (issuer matches the current zone’s token endpoint) could accept any UAA-signed JWT as an id_token without ensuring it was issued for the IdP’s configured relying party. The change aligns self-referencing IdP validation with the audience-binding behavior already applied to external OIDC providers.

Changes:

  • Enforce aud (audience) validation for self-referencing OIDC IdPs when a relying party client ID is configured.
  • Add integration tests proving rejection of UAA-signed tokens with the wrong aud and with no aud claim in the self-referencing IdP scenario.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java Adds audience enforcement for self-referencing OIDC IdPs to prevent accepting UAA-signed tokens issued to other clients.
server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerIT.java Adds regression tests covering wrong/missing audience for self-referencing OIDC IdP id_token validation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +776 to +781
if (hasLength(config.getRelyingPartyId())) {
// an explicitly registered self-referencing OIDC IdP must still bind the id_token
// to its own relying party, otherwise any token signed by this UAA instance
// (issued to any client, for any purpose) would be accepted as this IdP's id_token
jwtToken.checkAudience(config.getRelyingPartyId());
}
Comment on lines +557 to +558
// e.g. a plain access token response with no id_token/aud at all, as opposed to a real id_token
claims.put("sub", RandomStringUtils.random(50));
…check

The new audience check on self-referencing OIDC IdPs applied even when
the id_token arrived through the JWT Bearer grant or the password grant's
id_token exchange, both of which intentionally chain UAA-signed tokens
minted for other clients in the same zone. Those flows already
authenticate the calling client directly to /oauth/token, so binding the
presented token to the IdP's relying party doesn't apply the way it does
for an interactive browser login.

Only enforce the audience check when the caller supplied an explicit
origin, which is how the interactive callback (/login/callback/{origin})
always invokes this path; token-exchange callers omit the origin and let
it get resolved from the token's own issuer claim instead. Also swap
hasLength for hasText when checking whether a relying party ID is
configured, so a whitespace-only value doesn't count as configured.

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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java:273

  • The comment about the origin-omitted flow reads like it has a dangling/duplicated fragment ("minted for other clients…") and is a bit hard to follow. It would be clearer to rewrite it as a single coherent explanation of why relying-party audience binding is skipped only for self-referencing tokens when origin is omitted.
        // When origin is omitted (JWT Bearer/password-grant token exchange), we still validate audience for
        // external IdPs, but skip the relying-party audience binding for self-referencing (UAA-issued) tokens
        // to allow the intended token-chaining behavior.
        // minted for other clients in the same zone/origin - so it is exempt from that binding.

@strehle
strehle requested a lite review from Copilot August 27, 2026 15:36

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.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java:273

  • This comment block has a dangling/fragmented sentence ("minted for other clients…") and reads inconsistently. Rewording it into a single coherent explanation will make the audience-binding behavior easier to understand and avoid misleading future changes.
        // bind the presented id_token to that IdP's relying party (audience).
        // When origin is omitted (JWT Bearer/password-grant token exchange), we still validate audience for
        // external IdPs, but skip the relying-party audience binding for self-referencing (UAA-issued) tokens
        // to allow the intended token-chaining behavior.
        // minted for other clients in the same zone/origin - so it is exempt from that binding.


if (tokenEndpointBuilder.getTokenEndpoint(identityZoneManager.getCurrentIdentityZone()).equals(config.getIssuer())) {
List<SignatureVerifier> signatureVerifiers = getTokenKeyForUaaOrigin();
jwtToken = buildIdTokenValidator(idToken, new ChainedSignatureVerifier(signatureVerifiers), keyInfoService);
@duanemay
duanemay merged commit 49b5272 into develop Aug 27, 2026
28 checks passed
@duanemay
duanemay deleted the fix/self-oidc-audience-bypass branch August 27, 2026 16:02
@github-project-automation github-project-automation Bot moved this from Pending Merge | Prioritized to Done in Foundational Infrastructure Working Group Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

3 participants