feat(web): audit OAuth account-link conflicts - #1223
Conversation
When NextAuth would throw OAuthAccountNotLinked because a signed-in user attempts to add an OAuth identity that is already linked to a different Sourcebot user, emit an audit row with action `account.link_failed_already_linked` so the rejection is recoverable from the audit log instead of only the `[auth][error]` container logs. Fixes SOU-1184 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
WalkthroughThis PR adds OAuth/OIDC account-link conflict detection to the authentication flow. It extends the audit metadata schema with provider context fields, implements a pure conflict-detection utility with comprehensive tests, and integrates conflict auditing into the NextAuth ChangesOAuth Link Conflict Detection and Auditing
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes SOU-1184
Summary
callbacks.signInhook that detects the OAuth account-link conflict case (a currently signed-in user tries to authenticate with an OAuth identity whose(provider, providerAccountId)is already linked to a different Sourcebot user) and emits an audit row with actionaccount.link_failed_already_linked. The callback returnstrueunconditionally so@auth/corestill raisesOAuthAccountNotLinkeddownstream and the login-page error UX is unchanged.actor = attemptingUser,target = { id: alreadyLinkedToUserId, type: "user" }, andmetadata = { provider, providerAccountId }. Two new optional fields (provider,providerAccountId) were added toauditMetadataSchemato carry the upstream identity.computeOAuthLinkConflictAudithelper inauthUtils.tsso it can be unit-tested without mocking NextAuth's request scope. The I/O (Prisma lookup, session resolution, audit emission) lives in the signIn callback and is wrapped intry/catchso any failure stays best-effort and never blocks auth.Before this change, the rejection was only visible as
[auth][error]in the web container logs — a forensics gap for identity-hijack attempts.Test plan
yarn workspace @sourcebot/web test— 383/383 pass, including 7 new tests inauthUtils.test.tscovering: conflict detected foroauthandoidcaccounts, no audit when the same user re-authenticates, no audit when no session exists, no audit when the upstream identity is not yet linked, no audit for non-OAuth providers (credentials,email,webauthn), and no audit when account fields are missing.yarn workspace @sourcebot/web lint— no new errors.providerAccountIdalready belongs to user B, confirm a newaccount.link_failed_already_linkedrow appears in theAudittable with the expected actor/target/metadata.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests