Merge oauth2 accounts #26

Open
opened 2026-03-04 20:15:11 +00:00 by maxtkc · 1 comment
Owner

We need buttons to connect other logins from one login. By default, you login from one, it's kept separately. This would merge accounts. This is a slightly tricky db change

We need buttons to connect other logins from one login. By default, you login from one, it's kept separately. This would merge accounts. This is a slightly tricky db change
maxtkc self-assigned this 2026-03-04 20:15:11 +00:00
maxtkc added this to the GTFS Realtime project 2026-03-04 20:15:12 +00:00
Author
Owner

Courtesy of chat:

Safest Plan for Handling Multiple OAuth Providers (with Account Merging Caveat)

Websites supporting multiple OAuth providers (Google, GitHub, LinkedIn, etc.) use account linking and identity normalization. The core idea: maintain an internal user identity and map external OAuth identities to it.

Core Data Model

  • Users table

    • id (internal primary key)
    • profile fields (email, name, etc.)
  • OAuth identities table

    • id
    • user_id (FK → Users)
    • provider (google, github, apple…)
    • provider_user_id (stable subject from provider)
    • tokens/metadata
  • Feeds table

    • id
    • user_id (FK → Users)
    • other feed-specific fields

This allows one user to have many login methods and many feeds.

1. First-time OAuth Login

  • User clicks “Continue with Google.”
  • OAuth completes; site receives:
    • provider name
    • provider_user_id (OAuth sub)
    • email (maybe)
  • System checks OAuth identities table.
    • If not found:
      • Create new user
      • Create OAuth identity linked to that user

2. Returning with Same Provider

  • Find OAuth identity by (provider, provider_user_id)
  • Log in associated user

3. Linking a New Provider (Explicit Account Linking)

  • User logs in with existing method
  • In account settings: “Link GitHub”
  • OAuth completes
  • New OAuth identity is attached to the same user

Result:
One user, multiple providers.

Pros

  • Safest (no accidental merges)
  • Works with private/unverified emails

Cons

  • Extra step for user

Account Merging Caveat

  • If a user creates an account with one OAuth, creates a feed, then creates another account (with a different OAuth), and another feed, and later tries to merge:

    • All feeds and OAuth identities from the foreign account are moved to the currently logged-in account.
    • The foreign account is deleted in favor of the current one.
  • If a new account is about to be created and an existing account with the same email exists, suggest logging linking to the existing one.

Best Practices

  • Always key by (provider, provider_user_id), not email
  • Require linking from inside the account (login first, then link new provider)
  • Show clear messages if a new OAuth login matches an existing account:
    • “An account already exists with this email. Sign in and link your provider.”
  • Handle edge cases:
    • Apple private relay emails
    • GitHub noreply emails
    • User changes email at provider
    • User deletes/recreates provider account
    • Multiple providers with same email but different people
When user clicks OAuth button:
    lookup = find_identity(provider, provider_user_id)
    if lookup exists:
        log in user
    else:
        if user is currently logged in:
            link new identity to current user
        else:
            if verified_email matches existing user:
                ask user to confirm linking
            else:
                create new user

Account Merge Flow:

When merging accounts:
    For each feed and OAuth identity on foreign account:
        move to current account
    Delete foreign account

This balances user experience and security.

Courtesy of chat: # Safest Plan for Handling Multiple OAuth Providers (with Account Merging Caveat) Websites supporting multiple OAuth providers (Google, GitHub, LinkedIn, etc.) use **account linking** and **identity normalization**. The core idea: maintain an internal user identity and map external OAuth identities to it. ## Core Data Model - **Users table** - `id` (internal primary key) - profile fields (email, name, etc.) - **OAuth identities table** - `id` - `user_id` (FK → Users) - `provider` (google, github, apple…) - `provider_user_id` (stable subject from provider) - tokens/metadata - **Feeds table** - `id` - `user_id` (FK → Users) - other feed-specific fields This allows one user to have many login methods and many feeds. ## Recommended Login and Linking Flow ### 1. First-time OAuth Login - User clicks “Continue with Google.” - OAuth completes; site receives: - provider name - provider_user_id (OAuth sub) - email (maybe) - System checks OAuth identities table. - **If not found:** - Create new user - Create OAuth identity linked to that user ### 2. Returning with Same Provider - Find OAuth identity by (provider, provider_user_id) - Log in associated user ### 3. Linking a New Provider (Explicit Account Linking) - User logs in with existing method - In account settings: “Link GitHub” - OAuth completes - New OAuth identity is attached to the same user **Result:** One user, multiple providers. #### Pros - Safest (no accidental merges) - Works with private/unverified emails #### Cons - Extra step for user ## Account Merging Caveat - If a user creates an account with one OAuth, creates a feed, then creates another account (with a different OAuth), and another feed, and later tries to merge: - **All feeds and OAuth identities from the foreign account are moved to the currently logged-in account.** - The foreign account is deleted in favor of the current one. - If a new account is about to be created and an existing account with the same email exists, suggest logging linking to the existing one. ## Best Practices - **Always key by (provider, provider_user_id), not email** - Require linking from inside the account (login first, then link new provider) - Show clear messages if a new OAuth login matches an existing account: - “An account already exists with this email. Sign in and link your provider.” - Handle edge cases: - Apple private relay emails - GitHub noreply emails - User changes email at provider - User deletes/recreates provider account - Multiple providers with same email but different people ## Typical Production Flow (Recommended) ```pseudo When user clicks OAuth button: lookup = find_identity(provider, provider_user_id) if lookup exists: log in user else: if user is currently logged in: link new identity to current user else: if verified_email matches existing user: ask user to confirm linking else: create new user ``` **Account Merge Flow:** ```pseudo When merging accounts: For each feed and OAuth identity on foreign account: move to current account Delete foreign account ``` This balances user experience and security.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
gtfs.zone/deploy-gtfs-rt#26
No description provided.