Skip to content

feat(auth): link OAuth signin to existing local account with verified email #3493

Description

@PierreBrisorgueil

Symptom

User signs up locally with `brisorgueilp@gmail.com`. Later tries to sign in with Google using the same email. Result: Mongo E11000 duplicate key error on insert → OAuth callback fails → user is stuck.

Root cause

`modules/auth/controllers/auth.controller.js:303` — `checkOAuthUserProfile` looks up only by `(provider, providerData.sub)`. If no match, it falls through to `UserService.create` with the email. `users.model.mongoose.js:20` has `email: { unique: true }` → insert fails.

No account linking logic exists.

Fix

In `checkOAuthUserProfile`:

  1. Lookup by `(provider, providerData[key])` — current behavior.
  2. If no match, lookup by `email`.
  3. If match found AND `emailVerified: true` on the existing user → attach `providerData[key]` to existing user (patch it with `provider` + `providerData`), return it.
  4. If match found but `emailVerified: false` → reject with a clear error (avoid account takeover of unverified locals).
  5. If no email match → create as today.

Security rationale

Google and Apple guarantee `email_verified`. So linking to a local user who also verified their email is safe. Linking to an unverified local user would allow takeover: malicious signup with a victim's email (no verif), then victim signs in via Google → takes over the attacker-controlled doc.

Impact

  • Affects Google and Apple.
  • Critical UX: currently a user who forgot they already have a local account is permanently locked out.

Tests

  • Existing local user + verified → Google signin links and returns existing user.
  • Existing local user + unverified → Google signin rejected with 422.
  • No existing user → Google signin creates new user (current behavior).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions