Repository navigation
feat(auth): record owner-mailbox proof from OIDC logins - #1076
Merged
Merged
Conversation
A restricted account may email its verified owner mailbox, but proof was only ever written by the Google OAuth callback. The generic OIDC login now records it when the issuer-signed ID token asserts email_verified (JSON true) for an email equal to the account's current email. Adds oidc.scopes (E2A_OIDC_SCOPES, default [openid]) so deployments can request the email scope, and migration 132 allowing 'oidc' as a proof source. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Trim scope entries, reject internal whitespace and comma-only E2A_OIDC_SCOPES, document the email_verified trust requirement and the env var in deployment docs, and test numeric email_verified. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A restricted account may still email its verified owner mailbox (the sign-in email), but only if proof exists in
users.owner_email_verified_*. Today exactly one path writes it: the Google OAuth callback. The generic OIDC login never records proof and requests only theopenidscope, so on deployments where OIDC is the default sign-in most users never get proof and their agents cannot email them.The rule and trust argument
After the OIDC callback resolves a non-trashed user, if the ID token carries
email(string) andemail_verified(JSON booleantrue; the string"true"is not verified) and the trimmed/lower-cased claim equals the account's current email,RecordOIDCOwnerEmailProofwrites proof with sourceoidc.user_id_claimbinds the login to the account;email_verifiedis the IdP's assertion.deleted_at IS NULL AND lower(btrim(email)) = $2), same as the Google path.[auth] record owner email proof failed: user=<id> err=...and never fails the login. Email and raw claims are never logged. Mismatch/absent/unverified silently records nothing.Config
oidc.scopes/E2A_OIDC_SCOPES(comma-separated, trimmed). Default[openid]. Validation: no empty entries, no duplicates, must containopenid. Documented inconfig.example.yaml.Migration
132_owner_email_proof_oidc_source.sqlwidensusers_owner_email_verified_source_checkto('google_oauth', 'oidc'); idempotent.Backward compatibility
Default scopes are unchanged, so existing deployments see zero behaviour change. Adding
emailrequires the IdP client registration to allow it, otherwise logins fail withinvalid_scope.Tests
openid, empty entry, duplicate.RecordOIDCOwnerEmailProofsuccess/normalization, deleted account, mismatch, empty inputs, constraint still rejects unknown sources.async-message-pipeline.md,data-handling.md). No dashboard copy named Google as the only verifier, so web is untouched.Rollout for hosted deployments
Config may ship in either order relative to the binary: an older binary decodes the main config leniently and silently ignores
oidc.scopes/E2A_OIDC_SCOPES, continuing to requestopenid. The only hard ordering constraint is that the IdP's client registration must allowemailbefore any binary that requests it is live, or every OIDC login fails withinvalid_scope. Enableemailonly against an IdP that setsemail_verified: truesolely after a mailbox confirmation (see the trust warning inconfig.example.yamlanddocs/deployment.md).Merge note
Migration slot 132 is also claimed by open PRs #1075 and #1061; whichever merges second must renumber this file, because
internal/identity/migrate_test.gofails on a shared slot.🤖 Generated with Claude Code