Skip to content

Pass audience to verify_oauth2_token in get_email_from_bearer_token#5389

Open
herdiyana256 wants to merge 1 commit into
google:masterfrom
herdiyana256:fix-pubsub-push-audience-validation
Open

Pass audience to verify_oauth2_token in get_email_from_bearer_token#5389
herdiyana256 wants to merge 1 commit into
google:masterfrom
herdiyana256:fix-pubsub-push-audience-validation

Conversation

@herdiyana256

Copy link
Copy Markdown

`get_email_from_bearer_token` (src/appengine/libs/auth.py), the sole auth check for the `pubsub_push`-decorated `external_update` endpoint, called `id_token.verify_oauth2_token(token, google_requests.Request())` with no `audience` argument.

Per the function's own docstring, an omitted (`None`) audience means the audience/`aud` claim is not verified at all. The endpoint only checks that the token's `email` claim matches the App Engine default service account, which is commonly shared broadly across a GCP project. Without an audience check, any validly-signed ID token asserting that same service-account email, but minted for a completely different destination, would be accepted here.

Now passes `audience=request.url`, matching the URL Pub/Sub actually pushed to, which is what a correctly configured OIDC push subscription sets as the token's audience.

Added a test (`libs/auth_test.py`, new file — no existing test covered this function) confirming `verify_oauth2_token` is called with the request URL as audience, and that a wrong-audience token is rejected.

id_token.verify_oauth2_token() was called without an audience argument.
Per its own docstring, when audience is None the audience/aud claim is
not verified at all. This function is the sole auth check for the
pubsub_push-decorated external_update endpoint, gating on the token's
email claim matching the App Engine default service account.

Since audience wasn't checked, any validly-signed ID token asserting
that same service-account email would be accepted here regardless of
what destination it was actually minted for. The App Engine default
service account is commonly used broadly across a GCP project, so a
token obtained for an entirely different audience (another service or
endpoint authenticating with the same identity) could be replayed
against this endpoint.

Now passes audience=request.url, matching the URL Pub/Sub actually
pushed to, which is what a correctly configured OIDC push subscription
sets as the token's audience.
@herdiyana256
herdiyana256 requested a review from a team as a code owner July 25, 2026 15:52
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.

1 participant