Google 4: linking a Google identity to an existing account #343

Closed
opened 2026-09-10 08:12:57 -05:00 by bermudalamb · 0 comments
Owner

Part of #332. Phase 4 of six. Depends on #342.

The policy decided on #332, written down in code and pinned by tests. Small, and the most security-sensitive phase in the project.

The rule, in order

  1. An identity row matches. Sign in. Nothing else is consulted.
  2. No identity row, Google asserts email_verified: true, and a customer exists with that exact address. Create the identity against that customer and sign in.
  3. No identity row, Google does not assert a verified address, and a customer exists with that address. Refuse. Send them back to the password form with a message saying to sign in the way they usually do.
  4. No identity row and no matching customer. Create both, as #342 does.

Step 1 comes before the email comparison deliberately. An identity that has signed in before keeps working even if the address on either side has since changed.

Why auto-linking is safe here and only here

Google verifying the address means whoever completed that sign-in demonstrably controls the mailbox, which is already the root of trust for every other route into the account. Never link on an unverified address — that is a takeover path, and it is why this is a written rule rather than a default that arrives with a library.

Also in scope

  • Normalise the address the way registration does — lowercased and trimmed. A stricter comparison silently fails to link and produces a duplicate account instead of an error anyone sees.
  • Say what happened. Show the linked Google account on the account page, where the passkeys are listed. A silent link is indistinguishable from a bug when the customer later wonders why the password is no longer needed.

Done when

All four branches have a test; an unverified Google address never links; and linking is visible to the customer afterwards.

Part of #332. Phase 4 of six. Depends on #342. The policy decided on #332, written down in code and pinned by tests. Small, and the most security-sensitive phase in the project. ## The rule, in order 1. **An identity row matches.** Sign in. Nothing else is consulted. 2. **No identity row, Google asserts `email_verified: true`, and a customer exists with that exact address.** Create the identity against that customer and sign in. 3. **No identity row, Google does not assert a verified address, and a customer exists with that address.** **Refuse.** Send them back to the password form with a message saying to sign in the way they usually do. 4. **No identity row and no matching customer.** Create both, as #342 does. Step 1 comes before the email comparison deliberately. An identity that has signed in before keeps working even if the address on either side has since changed. ## Why auto-linking is safe here and only here Google verifying the address means whoever completed that sign-in demonstrably controls the mailbox, which is already the root of trust for every other route into the account. **Never link on an unverified address** — that is a takeover path, and it is why this is a written rule rather than a default that arrives with a library. ## Also in scope - **Normalise the address the way registration does** — lowercased and trimmed. A stricter comparison silently fails to link and produces a duplicate account instead of an error anyone sees. - **Say what happened.** Show the linked Google account on the account page, where the passkeys are listed. A silent link is indistinguishable from a bug when the customer later wonders why the password is no longer needed. ## Done when All four branches have a test; an unverified Google address never links; and linking is visible to the customer afterwards.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bermudalamb/redefined-designs#343