Skip to content

feat(token): support link_identity in the token-exchange grant#2614

Closed
spydon wants to merge 1 commit into
feat/facebook-access-token-grantfrom
feat/facebook-access-token-grant-link-identity
Closed

feat(token): support link_identity in the token-exchange grant#2614
spydon wants to merge 1 commit into
feat/facebook-access-token-grantfrom
feat/facebook-access-token-grant-link-identity

Conversation

@spydon

@spydon spydon commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What

Adds link_identity support to the token-exchange grant (#2609), so an already-authenticated user can attach a provider identity using a provider access token.

POST /token?grant_type=urn:ietf:params:oauth:grant-type:token-exchange
Authorization: Bearer <current user session>
{
  "provider": "facebook",
  "subject_token": "<facebook access token>",
  "subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "link_identity": true
}

Why

Parity with the id_token grant, which already supports link_identity. Lets a user who signed in another way connect their Facebook account natively (no browser). Because it only needs the verified provider subject, it works even when the app is already authorized (the token is a classic access token with no id token), which is the common case.

How

  • When link_identity is set, require a valid user access token in the Authorization header (requireAuthentication), resolve the target user, and reject the request if that user is banned (requireAuthentication itself does not check bans).
  • After verifying the provider token, branch to linkIdentityToUser (using the verified subject) instead of the sign-in-existing lookup. No profile claims are needed, only the subject from debug_token.

Limitations

  • Email-less target users (anonymous / phone-only with no email) cannot complete a link here: linkIdentityToUser requires an email to confirm and the Facebook access token carries none. Those users should link via the existing web flow (LinkIdentity, which fetches the profile via /me).
  • The linked identity's identity_data is sparse (only the subject), unlike the web flow which stores the full profile (email/name/avatar).

Tests

  • TestTokenExchangeLinkIdentity: links Facebook to an existing signed-in user; asserts the same user is returned and the facebook identity is attached.
  • TestTokenExchangeLinkIdentityRequiresAuth: linking without an Authorization header is rejected.
  • TestTokenExchangeLinkIdentityRejectsBannedUser: a banned user cannot link (and thus cannot re-establish a session).

Stacking

Stacked on #2609 (base branch feat/facebook-access-token-grant). Review/merge that first; this diff is only the linking addition.

@spydon
spydon requested a review from a team as a code owner July 3, 2026 08:04
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch from 5dc0147 to 1d3f849 Compare July 3, 2026 08:08
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch from a844510 to 44bc577 Compare July 3, 2026 08:08
Comment thread internal/api/token_access_token.go Outdated
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch from 1d3f849 to 16f15c6 Compare July 3, 2026 08:39
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch 2 times, most recently from e3d5fbe to 1d63179 Compare July 3, 2026 08:42
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch from 16f15c6 to 39ea58a Compare July 3, 2026 09:17
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch from 1d63179 to f709746 Compare July 3, 2026 09:18
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch 2 times, most recently from 9b63794 to f1c4695 Compare July 6, 2026 09:42
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch from f709746 to 0a12d3d Compare July 6, 2026 09:44
@spydon spydon changed the title feat(token): support link_identity in the access_token grant feat(token): support link_identity in the token-exchange grant Jul 6, 2026
Comment thread internal/api/token_exchange.go
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch from f1c4695 to 0280c58 Compare July 6, 2026 10:01
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch from 0a12d3d to 7b762bd Compare July 6, 2026 10:03
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch from 0280c58 to bbeefc9 Compare July 6, 2026 11:54
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch from 7b762bd to ef29762 Compare July 6, 2026 11:56
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch from 05f0a39 to abaae77 Compare July 7, 2026 19:57
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch from a0037f9 to 537c06a Compare July 7, 2026 19:57
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch from abaae77 to 835be4e Compare July 8, 2026 07:32
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch from 537c06a to 527af6b Compare July 8, 2026 07:35
@spydon
spydon force-pushed the feat/facebook-access-token-grant branch from 835be4e to d97b05b Compare July 14, 2026 07:50
When link_identity is set and a valid user access token is provided in the Authorization header, link the provider identity to that user instead of signing in an existing identity. The provider subject comes from the verified access token; no profile claims are needed. Banned users are rejected on this path too.
@spydon
spydon force-pushed the feat/facebook-access-token-grant-link-identity branch from 527af6b to aee6aec Compare July 14, 2026 08:23
@spydon

spydon commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Closing this since it wouldn't work for users that aren't logged in.

@spydon spydon closed this Jul 14, 2026
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