Skip to content

fix(invitations): pre-P9 hardening — legacy email normalization + best-effort finalize#3829

Merged
PierreBrisorgueil merged 4 commits into
masterfrom
fix/pre-p9-invitations-hardening
Jun 11, 2026
Merged

fix(invitations): pre-P9 hardening — legacy email normalization + best-effort finalize#3829
PierreBrisorgueil merged 4 commits into
masterfrom
fix/pre-p9-invitations-hardening

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Pre-P9 fixes (epic #3808) — Node PR-A

From the 2026-06-11 cross-PR review (plan infra/docs/superpowers/plans/2026-06-11-pre-p9-fixes-invitations.md). Both this and the Vue PR-B gate P9 (#3815).

T1 — 🔴 Migration: normalize legacy mixed-case emails (THE P9 blocker)

Post-chain, every email lookup lowercases its input and a plain findOne is a binary match — a legacy Pierre@Gmail.com row (possible pre-chain) becomes an unreachable account (signin/forgot/OAuth-link/E9 all miss it). The …120000 migration now lowercases mixed-case singles after the dup pre-check (which guarantees no collision) and before the index work. Pipeline $toLower, idempotent.
Edited in place (no timestamp bump) — deliberate: the migration has only ever run on ephemeral CI/per-pid test DBs; devkit has no prod; Trawl prod first runs it at P9.
Tests: raw-insert mixed-case row → up() → binary lowercase read-back; dup-laden DB still ABORTS with zero rows modified.

T2 — Best-effort finalize/release

A finalize DB hiccup after UserService.create could 422 an already-created account (user exists, can sign in; invite zombies until the sweep). Both finalize sites (signup + OAuth) now try/catch + logger.warn; the one unwrapped release mirrors the other 3. Test: mocked InvitationsService.accept rejection → signup still 200.

T3 — Doc fixes

2 stale "case-sensitive index" comments (P2-era, contradicted the P3 collation index) + the alias-removal TODO re-gated on P9 + the Vue verifyInvite repoint (executing the old TODO would have silently killed invite links — the Vue store still called the alias).

Verification

Lint clean; touched suites green (5/5 migration, 29/29 invitations integration); plan-anchored TDD (failing-first evidence per task); spec-reviewed against the plan.

Part of #3808. Pairs with Vue PR-B.

Summary by CodeRabbit

  • Bug Fixes

    • Improved invitation handling: signup invitations no longer fail if the finalization step encounters errors; users can complete registration successfully even if post-signup invitation cleanup fails.
  • Chores

    • Email normalization: email addresses are now automatically converted to lowercase for case-insensitive consistency.

Edited in place (no timestamp bump): the migration has only ever run on ephemeral
CI/per-pid test DBs — devkit has no prod, and Trawl prod first runs it at P9 (#3815).
Post-chain binary lookups (signin/forgot/OAuth-link/E9) lowercase their input;
un-normalized legacy rows would be unreachable accounts.
Copilot AI review requested due to automatic review settings June 11, 2026 11:59
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PierreBrisorgueil, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 15 minutes and 16 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 178ddffa-bcae-4c85-b442-145d8f89e57f

📥 Commits

Reviewing files that changed from the base of the PR and between 240c4fe and b4cacd0.

📒 Files selected for processing (3)
  • modules/auth/controllers/auth.controller.js
  • modules/auth/routes/auth.routes.js
  • modules/invitations/tests/invitations.integration.tests.js

Walkthrough

This PR hardens the invitation signup flow and email storage by normalizing mixed-case user emails to lowercase and wrapping invitation finalization operations in graceful error handling, ensuring registration succeeds even when post-signup invitation cleanup fails.

Changes

Invitations Hardening & Email CI Unique Index

Layer / File(s) Summary
Email case-insensitive index migration and tests
modules/users/migrations/20260610120000-users-email-ci-unique-index.js, modules/users/tests/users.email.ci.index.integration.tests.js, MIGRATIONS.md
Migration checks for true duplicate email variants, then normalizes all mixed-case email values to lowercase before applying case-insensitive collation index. Integration tests validate legacy mixed-case normalization and verify migration rejection of actual duplicate variants without modifying them.
Invitation finalization and claim release error handling
modules/auth/controllers/auth.controller.js, modules/invitations/tests/invitations.integration.tests.js
Signup and OAuth flows now wrap eligibility.finalize(...) and claim-release calls in try/catch blocks; finalization or release errors log warnings but allow successful registrations to return normally. Integration test validates signup returns 200 and creates user despite finalization throw.
Deprecation documentation update
modules/auth/routes/auth.routes.js
Updated TODO clarifies P9 timeline for auth routes alias deprecation, specifying that Trawl must absorb Vue P6 and Vue verifyInvite must repoint to /api/invitations/verify/:token before removal.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • pierreb-devkit/Node#3815: Modifications to the users-email-ci-unique-index migration and auth.controller.js invitation handling directly address the same email normalization and finalization error-handling hardening.
  • pierreb-devkit/Node#3811: Implements the invitations hardening (guarded finalize/claim-release) and users.email CI migration normalization behavior described in that issue.
  • pierreb-devkit/Node#3808: Both PRs change invitation handling in auth.controller and the users.email case-insensitive unique index migration.

Possibly related PRs

  • pierreb-devkit/Node#3821: Directly related—main PR's try/catch hardening around signup invite finalization builds on the two-phase invite claim/finalize/release contract changes in the same eligibility paths.
  • pierreb-devkit/Node#3824: Main PR's try/catch wrapping of invitation finalization/accept directly extends the retrieved PR's routing of invite finalization through InvitationsService.accept.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the two main changes: legacy email normalization (T1) and best-effort finalize handling (T2), accurately reflecting the primary objectives of the changeset.
Description check ✅ Passed The description covers most required template sections with clear context, rationale, and scope; however, the Validation checklist (npm run lint/test) is missing checkmarks, the Risk level is not explicitly stated, and some sections lack detail.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pre-p9-invitations-hardening

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.84%. Comparing base (1aacb58) to head (b4cacd0).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3829      +/-   ##
==========================================
+ Coverage   91.82%   91.84%   +0.01%     
==========================================
  Files         159      159              
  Lines        5201     5212      +11     
  Branches     1655     1659       +4     
==========================================
+ Hits         4776     4787      +11     
  Misses        337      337              
  Partials       88       88              
Flag Coverage Δ
integration 59.59% <100.00%> (+0.08%) ⬆️
unit 72.33% <7.14%> (-0.16%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9cc760d...b4cacd0. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the invitations/signup flow ahead of P9 by ensuring legacy mixed-case user emails remain reachable after the codebase’s “always-lowercase lookup” behavior, and by making invitation finalize/release steps best-effort so transient DB issues don’t turn successful account creation into an API error.

Changes:

  • Extend the users-email-ci-unique-index migration to lowercase legacy mixed-case emails after the duplicate pre-check and before index work.
  • Make invitation finalize (and one release site) best-effort in auth signup + OAuth paths, with warning logs instead of failing the request post-create.
  • Add/adjust integration tests to cover legacy email normalization and finalize failure resilience; update downstream-facing docs/comments.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
modules/users/tests/users.email.ci.index.integration.tests.js Adds integration coverage for legacy mixed-case email normalization + duplicate pre-check behavior.
modules/users/migrations/20260610120000-users-email-ci-unique-index.js Adds an (a2) normalization pass to lowercase legacy mixed-case emails before index operations.
modules/invitations/tests/invitations.integration.tests.js Adds integration coverage ensuring finalize failures don’t convert a successful signup into an error.
modules/auth/routes/auth.routes.js Updates TODO guidance for when the legacy /api/auth/invitations* alias can be safely removed downstream.
modules/auth/controllers/auth.controller.js Wraps invitation release/finalize calls in best-effort try/catch and logs warnings on finalize failures.
MIGRATIONS.md Documents that mixed-case single emails are now normalized in-place by the migration (no operator action required).

Comment thread modules/auth/controllers/auth.controller.js
Comment thread modules/auth/controllers/auth.controller.js
Comment thread modules/auth/controllers/auth.controller.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 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 `@modules/auth/controllers/auth.controller.js`:
- Around line 559-573: The logger.warn call in the oauth invite finalize block
should log the full error object so stack traces are captured; update the catch
block where oauthInvite is handled to pass finalizeErr (not
finalizeErr?.message) to logger.warn, keeping the same message and metadata
(referencing oauthEligibility.finalize, createdUser._id/createdUser.id and the
userId field) so the full error object is available in logs for debugging.
- Around line 203-218: In the catch block after calling eligibility?.finalize in
the if (invite && !config.sign.up) path, change the logger.warn payload so it
passes the full Error object (finalizeErr) instead of only finalizeErr?.message;
update the logger.warn call in the catch for eligibility.finalize to include
err: finalizeErr (and keep userId as String(user._id || user.id)) so Winston can
capture the stack and structured error details.

In `@modules/auth/routes/auth.routes.js`:
- Around line 12-15: The TODO extending removal from P6 to P9 keeps the core
auth module dependent on the optional invitations module longer than intended;
revert the timeline back to P6 (restore the original target milestone) and
update the comment to include the tracking issue number
(pierreb-devkit/Node#3815), the exact prerequisites (downstream Trawl update and
Vue auth.store repointing of verifyInvite), and an owner/ETA so this
technical-debt coupling is timeboxed; ensure the note explicitly calls out the
two imports and the three alias routes in this file so they can be removed when
the prerequisites are met.
- Around line 12-15: Update the inconsistent TODO block so both references use
the same deprecation target: change the inline phrase "temporary coupling until
P6 removes these routes" to reference P9 to match the TODO header
(TODO(P9,...)), ensuring the comment consistently indicates removal after P9 and
clarifying the conditions (downstream Trawl update and Vue auth.store
`verifyInvite` repointing) in the same sentence; locate the TODO comment and the
subsequent explanatory sentence containing "temporary coupling until P6 removes
these routes" and make the phrasing uniform (both P9) and grammatically
consistent.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1f54c4bc-a50a-49e4-be25-6c508514e2aa

📥 Commits

Reviewing files that changed from the base of the PR and between 9cc760d and 240c4fe.

📒 Files selected for processing (6)
  • MIGRATIONS.md
  • modules/auth/controllers/auth.controller.js
  • modules/auth/routes/auth.routes.js
  • modules/invitations/tests/invitations.integration.tests.js
  • modules/users/migrations/20260610120000-users-email-ci-unique-index.js
  • modules/users/tests/users.email.ci.index.integration.tests.js

Comment thread modules/auth/controllers/auth.controller.js
Comment thread modules/auth/controllers/auth.controller.js
Comment thread modules/auth/routes/auth.routes.js
…arms

Addresses review threads: the capacity-gate release swallow is now logged,
both finalize warns include the stack, and the alias-coupling comment is
gated on P9. Adds OAuth-finalize-throw + cap-release-throw integration tests
covering the 3 codecov-missed catch lines.
@PierreBrisorgueil PierreBrisorgueil merged commit 77330f2 into master Jun 11, 2026
8 checks passed
@PierreBrisorgueil PierreBrisorgueil deleted the fix/pre-p9-invitations-hardening branch June 11, 2026 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants