Prompt
What should I use for secure account linking across providers?
Latest observation
For secure account linking across identity providers, the safest general pattern is:
Use a single internal user ID plus a separate identity-link table
Do not merge accounts just because emails match.
Recommended model
- User: your app’s canonical account
- Identity: one row per external provider login
- provider name:
google,apple,github, etc. - provider subject/user ID: the provider’s stable unique identifier (
sub,id, etc.) - linked
user_id - optional metadata: email, email_verified, created_at, last_login_at
- provider name:
This lets one user have multiple linked identities safely.
Safe linking rules
Link accounts only when you have strong proof the same person controls both identities.
Best options, in order:
-
User is already authenticated in your app
- Then they explicitly click “Link Google / Apple / GitHub”
- Start an OAuth/OIDC flow for that provider
- On callback, link the returned provider identity to the currently signed-in internal user
-
Verified email + fresh re-authentication
- If you allow auto-suggestion based on email, only do it when:
- the provider asserts
email_verified = true - and the user re-enters credentials / completes MFA / confirms via in-app prompt
- the provider asserts
- Even then, avoid silent linking; use confirmation.
- If you allow auto-suggestion based on email, only do it when:
-
Out-of-band verification
- Send a magic link or confirmation code to an existing verified channel before linking.
What to use technically
Prefer:
- OAuth 2.0 + OpenID Connect (OIDC) for login/linking
- Store the provider’s stable subject identifier (
sub) as the real external identity key - Use PKCE for public clients
- Use state and nonce to prevent CSRF/replay
Avoid:
- Linking solely by email
- Using display names or usernames as identifiers
- Trusting unverified email from the provider
- Using mutable identifiers as the primary linkage key
Security checks you should enforce
- Verify the OIDC ID token signature and issuer
- Validate:
issaudexpnonce(for login flows)
- For OAuth linking flows:
- use
state - bind the flow to the current logged-in session
- use
- Require step-up auth before linking sensitive providers or when changing a primary login
- Log link/unlink events for audit
- Provide a way to unlink identities
Practical policy
A good default policy is:
- Login: find user by
(provider, provider_subject) - Linking: only link when the user is already authenticated in your app
- Fallback: if an unrecognized provider login has the same verified email as an existing account, prompt the user to sign in to that account first, then link manually
Short answer
Use OIDC/OAuth with PKCE, and link identities by provider + stable provider user ID, not by email. Make account linking an explicit, authenticated action by an already signed-in user, with verified email only as a supporting signal—not the sole basis.
If you want, I can give you a concrete database schema and linking flow for Google/Apple/GitHub.