Skip to content

fix(auth): require audience when introspecting via Google tokeninfo - #6236

Open
SashaMIT wants to merge 2 commits into
stacklok:mainfrom
SashaMIT:fix/google-tokeninfo-audience
Open

fix(auth): require audience when introspecting via Google tokeninfo#6236
SashaMIT wants to merge 2 commits into
stacklok:mainfrom
SashaMIT:fix/google-tokeninfo-audience

Conversation

@SashaMIT

@SashaMIT SashaMIT commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Problem

The Google tokeninfo path has a validation hole:

  1. Google's tokeninfo endpoint returns no iss claim. GoogleProvider.parseGoogleResponse (token.go) compensates by setting claims["iss"] = "https://accounts.google.com" locally.
  2. validateClaims then compares that fabricated value against the configured issuer — a check that is self-satisfying: it passes for every token tokeninfo accepts and proves nothing about which OAuth client the token was minted for.
  3. The only remaining binding to the deployment is the audience check, and it is conditional: if v.audience != "". Audience is optional everywhere — NewTokenValidator doesn't require it, --oidc-audience defaults to empty, the operator CRD marks it Optional.

Net effect: a deployment configured with issuer https://accounts.google.com + introspection URL https://oauth2.googleapis.com/tokeninfo + no audience accepts any valid Google OAuth access token — including one minted for an unrelated attacker-registered OAuth client. That token passes ValidateToken, becomes an auth.Identity, and lands in the request context.

Fix

NewTokenValidator refuses IntrospectionURL == GoogleTokeninfoURL with an empty audience at startup, with an error explaining why. Fail-closed beats a silently-accepting runtime.

Tests

New TestNewTokenValidator_GoogleTokeninfoRequiresAudience: tokeninfo-without-audience is rejected with the explanatory error; the same config with an audience is accepted. Full pkg/auth suite passes.

Made with Cursor

Made with Cursor

Google's tokeninfo response carries no iss claim; the GoogleProvider
synthesises iss locally, so a configured-issuer check against it is
self-satisfying and proves nothing about which OAuth client the token
was minted for. The only real binding to the deployment is the audience
check - which is skipped when audience is empty. A deployment pointed
at https://oauth2.googleapis.com/tokeninfo with no audience accepted
ANY valid Google access token, including one minted for an unrelated
attacker-controlled OAuth client.

NewTokenValidator now refuses that combination at startup instead of
silently accepting cross-client tokens at runtime.
jhrozek
jhrozek previously approved these changes Aug 7, 2026

@jhrozek jhrozek 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.

Nice fix, this closes a real gap — tokeninfo doesn't give you a verifiable iss so audience was the only thing actually binding the token to this deployment. Two small optional nits below, neither blocking.

Comment thread pkg/auth/token.go Outdated
Comment thread pkg/auth/token_test.go
@github-actions github-actions Bot added the size/XS Extra small PR: < 100 lines changed label Aug 10, 2026
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.87%. Comparing base (04198d4) to head (dfa64a8).
⚠️ Report is 47 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6236      +/-   ##
==========================================
+ Coverage   72.54%   72.87%   +0.32%     
==========================================
  Files         739      742       +3     
  Lines       76851    77786     +935     
==========================================
+ Hits        55755    56688     +933     
+ Misses      17113    17107       -6     
- Partials     3983     3991       +8     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Extract JWKS-discovery resolution and the Google tokeninfo audience
check into their own functions so NewTokenValidator's cyclomatic
complexity drops back under the gocyclo threshold, which CI was
failing on. Also apply the two open review nits: trim whitespace on
the audience check and make the new test table-driven.
@SashaMIT

Copy link
Copy Markdown
Contributor Author

Thanks for landing the nits yourself. Trim on audience and the table-driven test are the right shape, and pulling the Google tokeninfo check out of NewTokenValidator is cleaner than fighting gocyclo.

Happy to leave the rest with you.

@jhrozek jhrozek 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.

Both nits addressed (helper extraction fixes the gocyclo lint failure too). CI green aside from the pre-existing, unrelated GitHub Actions Static Analysis failure that's also failing on main.

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

Labels

size/XS Extra small PR: < 100 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants