fix: return 409 when unverified OAuth email conflicts with existing account
Before: branch 3 of oauth_callback attempted to INSERT a user with a duplicate email → DB constraint violation → 500. After: if email_verified=False and the email already exists, raise 409 with a message directing the user to sign in with their password. Also adds test_callback_unverified_email_conflict_returns_409. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -460,6 +460,17 @@ async def oauth_callback(
|
|||||||
await db.commit()
|
await db.commit()
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
|
# Guard: if the email is already taken but we couldn't auto-link (e.g.
|
||||||
|
# email_verified=False), refuse with 409 instead of hitting a DB constraint.
|
||||||
|
if not userinfo.email_verified:
|
||||||
|
conflict = await db.execute(select(User).where(User.email == userinfo.email))
|
||||||
|
if conflict.scalar_one_or_none() is not None:
|
||||||
|
raise HTTPException(
|
||||||
|
status.HTTP_409_CONFLICT,
|
||||||
|
"An account with this email already exists. "
|
||||||
|
"Please sign in with your password.",
|
||||||
|
)
|
||||||
|
|
||||||
# 3. New user — social-only account (no password).
|
# 3. New user — social-only account (no password).
|
||||||
new_user = User(
|
new_user = User(
|
||||||
id=str(uuid.uuid4()),
|
id=str(uuid.uuid4()),
|
||||||
|
|||||||
@@ -341,3 +341,18 @@ class TestOAuth:
|
|||||||
oauth_sub = self._decode_sub(resp.json()["access_token"])
|
oauth_sub = self._decode_sub(resp.json()["access_token"])
|
||||||
# OAuth login must resolve to the same user as the original registration
|
# OAuth login must resolve to the same user as the original registration
|
||||||
assert orig_sub == oauth_sub
|
assert orig_sub == oauth_sub
|
||||||
|
|
||||||
|
def test_callback_unverified_email_conflict_returns_409(self, client, monkeypatch) -> None:
|
||||||
|
"""Unverified Google email matching an existing account returns 409, not 500."""
|
||||||
|
email = "conflict@example.com"
|
||||||
|
reg_resp = client.post(
|
||||||
|
"/api/v1/auth/register",
|
||||||
|
json={"email": email, "password": "TestPass123!"},
|
||||||
|
)
|
||||||
|
assert reg_resp.status_code == 201
|
||||||
|
|
||||||
|
self._patch_google(monkeypatch)
|
||||||
|
state = self._authorize(client)
|
||||||
|
resp = self._callback(client, state, self._userinfo(email=email, email_verified=False))
|
||||||
|
|
||||||
|
assert resp.status_code == 409
|
||||||
|
|||||||
Reference in New Issue
Block a user