feat(token): add RFC 8693 token-exchange grant for provider access tokens#2609
Open
spydon wants to merge 1 commit into
Open
feat(token): add RFC 8693 token-exchange grant for provider access tokens#2609spydon wants to merge 1 commit into
spydon wants to merge 1 commit into
Conversation
This was referenced Jul 2, 2026
This was referenced Jul 3, 2026
5dc0147 to
1d3f849
Compare
1d3f849 to
16f15c6
Compare
9b63794 to
f1c4695
Compare
f1c4695 to
0280c58
Compare
05f0a39 to
abaae77
Compare
…kens Adds grant_type=urn:ietf:params:oauth:grant-type:token-exchange so a client can sign in with a provider-issued access token (subject_token) instead of an OIDC id token. Facebook's native Android login only mints an OIDC id token (carrying the email claims) on the first authorization, so signup stays on the id_token grant. On subsequent logins Facebook returns only a classic access token with no profile claims. This grant verifies the token belongs to the app via a provider-specific AccessTokenVerifier (Facebook: /debug_token, checking app id, validity and user-token type), reads the provider subject from it, and signs in the existing identity. It does not create accounts.
abaae77 to
835be4e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an RFC 8693 token-exchange grant to
POST /tokenso a client can sign in with a provider-issued access token instead of an OIDC id token:Why
Native Facebook login on Android (supabase/supabase-flutter#1287) only mints an OIDC id token (which carries
email/email_verified) on the first authorization. Every subsequent login returns a classic access token with no profile claims and no id token, so theid_tokengrant is unusable for the common repeat-login case. This is a Facebook platform limitation (Limited Login is iOS-only). See facebook/facebook-android-sdk#1132.The split, matching how Firebase and Auth0 handle Facebook:
id_tokengrant (has the email claims).How
grant_typeis the standard RFC 8693 URN; the provider-specific bit is thesubject_token_type+provider(matched case-insensitively), so new providers opt in rather than needing a new grant (the design the reviewer asked for; happy to foldproviderintosubject_token_typeif preferred).provider.AccessTokenVerifierinterface. Facebook's implementation calls/debug_token(app token sent as a Bearer header, not in the URL; transport errors have the URL stripped so the token can't leak), and requiresis_valid, anapp_idmatching this app, and aUSERtoken type. It returns the token'suser_idas the subject.debug_tokenreturns no profile claims, so the grant looks up the existingauth.identitiesrow (provider,provider_id=user_id) and issues a session. It does not create accounts (a brand-new user signs up via the id-token first-login path), and rejects banned users and unconfirmed users (unlessAllowUnverifiedEmailSignInsis set), matching the other sign-in paths. A missing identity returns a clear error; the client fallback for that edge case issignInWithOAuth(web). All sign-in failures return a genericInvalid subject_tokento avoid account enumeration.Tests
internal/api/token_exchange_test.go: sign-in of an existing identity, case-insensitive provider, no-identity rejected, banned user rejected, unconfirmed user rejected, unconfirmed allowed whenAllowUnverifiedEmailSignInsis set, token from another app rejected, invalid token, non-user token, missing subject_token / provider, unsupported subject_token_type, unsupported provider, disabled provider. All pass against Postgres.Notes / follow-ups
VerifyAccessTokenverifies against the primary configured Facebook client id only. Multiple client ids per project are out of scope for this grant (single-app is the expected config); theid_tokengrant's multi-id handling is unaffected.subject_token_typefor defense in depth once the client can produce one; the server flow is identical.link_identitysupport is stacked in feat(token): support link_identity in the token-exchange grant #2614.