fix(identity): normalize blank email/phone to NULL before persisting - #850
Open
libingtong wants to merge 1 commit into
Open
fix(identity): normalize blank email/phone to NULL before persisting#850libingtong wants to merge 1 commit into
libingtong wants to merge 1 commit into
Conversation
identities.email and identities.phone are both `unique=True` and nullable.
Postgres allows many NULLs in a unique index but only a single empty string, so
writing "" makes the *second* identity that lacks the field fail with:
duplicate key value violates unique constraint "ix_identities_email"
ExternalUserInfo defaults email and mobile to "" (not None), and
BaseAuthProvider._create_new_user passes them straight to
find_or_create_identity, which forwards them to identity_dao.create_identity.
Any SSO provider whose upstream directory does not expose an email address
therefore works for exactly one user and then breaks for everyone after.
Observed in production with an enterprise directory that returns neither email
nor mobile for some members: the first such user signs in fine, the second gets
a 500 from the OAuth callback.
Normalizing at the entry point of find_or_create_identity covers both the lookup
and the insert path. It also prevents "" from being used as a lookup key, which
would otherwise merge every contact-less user into one identity — the function's
own comment notes that only email and phone are authoritative ownership claims,
and an empty string is not a claim.
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.
identities.emailandidentities.phoneare bothunique=Trueand nullable. Postgres allows many NULLs in a unique index but only a single empty string, so writing""makes the second identity that lacks the field fail with:How it happens
ExternalUserInfodefaultsemailandmobileto""(notNone), andBaseAuthProvider._create_new_userpasses them straight tofind_or_create_identity, which forwards them toidentity_dao.create_identity.Any SSO provider whose upstream directory does not expose an email address therefore works for exactly one user and then breaks for everyone after.
Observed
With an enterprise directory that returns neither email nor mobile for some members: the first such user signs in fine, the second gets a 500 from the OAuth callback (
OAuth 登录失败 / Internal server erroron the callback page).This is easy to miss during integration testing because it only appears once a second contact-less user tries to sign in.
This PR
Normalizes at the entry point of
find_or_create_identity, covering both the lookup and the insert path.It also prevents
""from being used as a lookup key — which would otherwise merge every contact-less user into a single identity. The function's own comment notes that only email and phone are authoritative identity claims, and an empty string is not a claim.Existing rows already containing
""should be normalized to NULL as a one-off (UPDATE identities SET email = NULL WHERE email = ''), otherwise they keep occupying the single allowed empty-string slot.