Account linking
Change email vs link email, OAuth provider linking, provider trust, and guardrails.
Overview
Account linking lets users attach additional sign-in methods (email, OAuth providers, wallets) to an existing account. Two primary patterns exist: change email (replace primary email) and link email / OAuth (add methods without removing existing ones).
Change email vs link email
| Capability | Change email | Link email |
|---|---|---|
| Effect | Replaces users.email | Adds a linked method; primary email unchanged |
| Flow | Request → verify code/link → update | Request → verify link → link account |
| Use case | User changes email address | User adds email sign-in to OAuth-only account |
Change email uses change_email verification type; link email uses link_email. Both use the same 6-digit + link UX and 15-minute TTL.
OAuth provider linking
Logged-in users can link additional OAuth providers from Profile (Settings). The backend:
- Uses
oauth_link_state(distinct fromoauth_statefor sign-in) - Binds the link to
meta.userIdso the exchange attaches to the correct account - Stores
meta.redirectUrifor Google (fromredirect_uriquery) when multiple callback URLs are configured (web + mobile) - Returns 409
PROVIDER_ALREADY_LINKEDwhen the provider is linked to another user - Returns
redirectToso the client can return the user to the settings page after success
For Google, when using multiple callback URLs (e.g. web + mobile app), the client passes redirect_uri in the link-authorize-url request. It must be in the allowlist (OAUTH_GOOGLE_CALLBACK_URLS). See Authentication for full Google OAuth setup.
Provider trust
OAuth providers are trusted for identity on first link. Subsequent links require re-authorization; existing tokens are replaced on each successful link.
Guardrail: last sign-in method
Users must keep at least one sign-in method. Unlinking (wallet, OAuth, or other methods) fails with 400 LAST_SIGN_IN_METHOD if it would leave no way to sign in.
Verify modes (change email)
Verification accepts exactly one of:
{ token, email }— code entry flow; email used as identifier{ token, verificationId }— link-click flow; verificationId from callback URL
See Authentication for full flow details.
Authentication
JWT-based auth with access + refresh tokens. Supports magic links, OAuth providers, Web3 wallet sign-in, and API keys for programmatic access.
Frontend Architecture
Next.js (App Router, RSC-first) with shadcn/ui + Tailwind CSS in @repo/ui and type-safe API consumption via @repo/core + @repo/react.