fix(profile): safely handle already-linked identity conflicts#1056
Conversation
Return a generic message from the send-code and verify endpoints when an email is already linked to another account, and log the resolved account server-side. Surface the error in the add-account dialog's email step so the failure is visible to the user. Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> Jira: LFXV2-2569
Extract the generic already-linked response string into EMAIL_ALREADY_LINKED_MESSAGE in the shared profile constants and reuse it in both the send-code and verify branches, so the two responses stay in sync. Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> Jira: LFXV2-2569
The identity-link call returns "the provided identity token belongs to an existing LFID account and cannot be linked", which has no "already", so the social callback misclassified it as a generic link_failed toast instead of the already-linked conflict banner. Centralize conflict detection in isIdentityAlreadyLinkedError and apply it to the send-code, verify, and social-callback paths. Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> Jira: LFXV2-2569
|
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 (3)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughAdds a shared helper and message constant for already-linked identity errors, updates profile controller flows to use them, and shows an inline add-account dialog error when verification fails before a code is sent. ChangesAlready-linked identity error handling
Estimated code review effort: 3 (Moderate) | ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Comment |
There was a problem hiding this comment.
Pull request overview
This PR hardens and unifies “identity already linked” conflict handling across the shared utilities, the profile server controller, and the add-account dialog UI, with an emphasis on preventing account enumeration while still enabling server-side support diagnostics.
Changes:
- Centralized upstream conflict detection in
isIdentityAlreadyLinkedError(shared util) and added unit tests. - Introduced
EMAIL_ALREADY_LINKED_MESSAGEand updated profile controller endpoints to return a generic message while logging the resolved owning account server-side. - Surfaced send-code failures in the add-account dialog’s email-entry step so conflicts aren’t silently ignored.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/src/utils/identity.utils.ts | Adds shared marker-based helper to detect already-linked identity/email conflicts across differing upstream phrasings. |
| packages/shared/src/utils/identity.utils.spec.ts | Adds Vitest coverage for the new conflict detector and existing email-shape helper. |
| packages/shared/src/constants/profile.constants.ts | Adds a reusable generic “email already linked” client message to avoid account enumeration. |
| apps/lfx-one/src/server/controllers/profile.controller.ts | Uses shared conflict detection, returns generic conflict messaging (409) for send-code + verify, and logs resolved account server-side for support. |
| apps/lfx-one/src/app/modules/profile/components/add-account-dialog/add-account-dialog.component.html | Displays verification/send errors during the email-entry step (previously silent). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/lfx-one/src/server/controllers/profile.controller.ts (1)
1925-1927: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMissing support-log parity with
sendEmailVerification.
sendEmailVerification's already-linked branch resolves and logs the linked account (linked_to) for support debugging.verifyAndLinkEmail's equivalent branch returns the generic message directly without the same resolution/logging, losing support visibility when the conflict surfaces at the verify step instead of send-code.♻️ Proposed fix for parity
if (!linkResponse.success) { if (isIdentityAlreadyLinkedError(linkResponse.error, linkResponse.message)) { + const linkedTo = + (await this.emailVerificationService.resolveEmailToUsername(req, email)) || (await this.emailVerificationService.resolveEmailToSub(req, email)); + logger.warning(req, 'verify_and_link_email', 'Email already linked to another account', { email, linked_to: linkedTo }); // Never forward the upstream message here — it could name the owning account. res.status(409).json({ success: false, error: linkResponse.error, message: EMAIL_ALREADY_LINKED_MESSAGE });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/lfx-one/src/server/controllers/profile.controller.ts` around lines 1925 - 1927, The already-linked branch in verifyAndLinkEmail lacks the same support-log parity as sendEmailVerification, so add the linked-account resolution and logging before returning the 409 response. Use the existing identity-link flow helpers around isIdentityAlreadyLinkedError and the linkResponse handling to resolve the owning account into linked_to, log it for support debugging, and still return the generic EMAIL_ALREADY_LINKED_MESSAGE without exposing upstream details.apps/lfx-one/src/app/modules/profile/components/add-account-dialog/add-account-dialog.component.html (1)
95-98: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd an ARIA live region for the new error message.
The error
<p>has norole="alert"/aria-live, so screen reader users won't be notified when the already-linked error appears.♿ Proposed fix
`@if` (!codeSent() && verificationError()) { - <p class="text-sm text-red-600" data-testid="add-account-send-error">{{ verificationError() }}</p> + <p class="text-sm text-red-600" role="alert" aria-live="polite" data-testid="add-account-send-error">{{ verificationError() }}</p> }As per path instructions,
apps/lfx-one/src/app/**/*.component.{ts,html}changes implementing error states should "add ARIA roles, focus management, ... interactive states (hover, active, loading, error, empty)".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/lfx-one/src/app/modules/profile/components/add-account-dialog/add-account-dialog.component.html` around lines 95 - 98, The new verification error message in add-account-dialog.component.html needs an ARIA live region so screen readers announce it when it appears. Update the error <p> rendered by the verificationError() branch to include an appropriate alert/live attribute (for example, role="alert" or aria-live) while keeping the existing data-testid and conditional rendering intact. Use the add-account-dialog.component.html template and the verificationError() / codeSent() block to locate the change.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/lfx-one/src/server/controllers/profile.controller.ts`:
- Around line 1796-1809: The 409 response in profile.controller.ts still exposes
the raw upstream error via response.error, which can override the intended
EMAIL_ALREADY_LINKED_MESSAGE in add-account-dialog.component.ts. Update the
send_email_verification and related linked-account response paths to return a
stable error code or a sanitized error value instead of
response.error/linkResponse.error, and ensure the client-side handling in
add-account-dialog.component.ts prefers the response message for this case.
---
Nitpick comments:
In
`@apps/lfx-one/src/app/modules/profile/components/add-account-dialog/add-account-dialog.component.html`:
- Around line 95-98: The new verification error message in
add-account-dialog.component.html needs an ARIA live region so screen readers
announce it when it appears. Update the error <p> rendered by the
verificationError() branch to include an appropriate alert/live attribute (for
example, role="alert" or aria-live) while keeping the existing data-testid and
conditional rendering intact. Use the add-account-dialog.component.html template
and the verificationError() / codeSent() block to locate the change.
In `@apps/lfx-one/src/server/controllers/profile.controller.ts`:
- Around line 1925-1927: The already-linked branch in verifyAndLinkEmail lacks
the same support-log parity as sendEmailVerification, so add the linked-account
resolution and logging before returning the 409 response. Use the existing
identity-link flow helpers around isIdentityAlreadyLinkedError and the
linkResponse handling to resolve the owning account into linked_to, log it for
support debugging, and still return the generic EMAIL_ALREADY_LINKED_MESSAGE
without exposing upstream details.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a9fc7257-b15c-4b20-84bf-6248b78ceddd
📒 Files selected for processing (5)
apps/lfx-one/src/app/modules/profile/components/add-account-dialog/add-account-dialog.component.htmlapps/lfx-one/src/server/controllers/profile.controller.tspackages/shared/src/constants/profile.constants.tspackages/shared/src/utils/identity.utils.spec.tspackages/shared/src/utils/identity.utils.ts
Address review comments from coderabbitai: - add-account-dialog.component.ts: prefer response.message over response.error in the send/verify/resend success:false callbacks so the generic message wins - profile.controller.ts: log the resolved owning account (linked_to) in verifyAndLinkEmail's already-linked branch, matching sendEmailVerification - add-account-dialog.component.html: add role="alert" to the send/OTP error messages so screen readers announce them Resolves 3 review threads. Signed-off-by: Fayaz G <5818912+fayazg@users.noreply.github.com> Jira: LFXV2-2569
Review Feedback AddressedCommit: f5ddf23 Changes Made
Threads Resolved1 of 1 review threads addressed; the two nitpicks from the review body are addressed in the same commit. |
|
Hey @fayazg 👋 — reviewed this round (commit Overall impression: This is a clean, well-scoped fix. The core mitigation — never surfacing the owning account's identity to the client while still logging it server-side for support — is applied consistently across all three conflict paths (send-code, verify-and-link, and the social auth callback). The centralized I ran two independent subagent passes (security-focused and general code-quality) in addition to my own review across all changed files. Revision tracking — round 1 → round 2:
No regressions introduced by the fix-up commit. I agree with Copilot's clean pass on this PR. One thing I'd note but don't think needs action: CodeRabbit's original ARIA suggestion also proposed Security pass: No enumeration vector in any of the three paths (send-code, verify-and-link, social callback) — all return the generic message/redirect code, never the owning account. Auth/CSRF checks are intact. One secondary observation: the Code quality: One optional nit — the three-line Issue count:
Final decision: ✅ Approved Nice work tightening this up — the enumeration fix is solid and the round-1 feedback was addressed thoroughly. |
Summary
isIdentityAlreadyLinkedErrorutil (with unit tests) and reuse a sharedEMAIL_ALREADY_LINKED_MESSAGEconstant across paths.Fixes LFXV2-2569
🤖 Generated with Claude Code