Skip to content

refactor(client): drop misc Meteor type/value imports from 2FA + startup#40556

Merged
ggazzo merged 6 commits into
developfrom
refactor/drop-misc-meteor-imports
May 18, 2026
Merged

refactor(client): drop misc Meteor type/value imports from 2FA + startup#40556
ggazzo merged 6 commits into
developfrom
refactor/drop-misc-meteor-imports

Conversation

@ggazzo
Copy link
Copy Markdown
Member

@ggazzo ggazzo commented May 15, 2026

Draft — three small cleanups grouped to avoid one-liner PR spam.

Summary

  • `lib/2fa/utils.ts` only imported `import type { Meteor }` to spell `Meteor.Error` inside type-guard return types. The guards check `.error` / `.errorType` structurally — never `instanceof` — so a local `MeteorErrorLike = Error & { error?; errorType?; reason?; details? }` type covers both `Meteor.Error` and `Meteor.TypedError` shapes without pulling `meteor/meteor` for types.
  • `lib/2fa/overrideLoginMethod.ts` switches its `LoginError` alias from `globalThis.Error | Meteor.Error | Meteor.TypedError` to `MeteorErrorLike` for the same reason. The `Accounts` value import stays — `Accounts.callLoginMethod` migrates separately.
  • `lib/2fa/process2faReturn.ts` narrows `hasRequiredTwoFactorMethod` and the public function signatures to `MeteorErrorLike` so the new local type composes through. The `Meteor` value import stays for the runtime `new Meteor.Error('totp-canceled')` thrown when the 2FA modal closes — replacing that requires auditing the catch sites and is out of scope.
  • `meteor/startup/absoluteUrl.ts` patches `Meteor.absoluteUrl` / `Meteor._relativeToSiteRootUrl` on the client globals. Zero callers read either on the client (verified by grep — every consumer is server-side, where Meteor's own absoluteUrl runs). The file already carried a TODO marking itself as dead once DDP is gone. Delete it.

Test plan

  • `yarn eslint` on the 4 changed files + new types.ts: 0 errors.
  • `tsc --noEmit` on `apps/meteor` is clean.
  • `grep -rn "Meteor\.absoluteUrl\ |Meteor\._relativeToSiteRootUrl" apps/meteor/{client,app/*/client,ee/client}` returns zero non-comment hits.
  • Manual: trigger the 2FA modal (admin enable Accounts_TwoFactorAuthentication_Enabled, log in with TOTP); enter valid + invalid + max-attempts codes; close the modal. The type guards still narrow the rejection paths correctly.

Task: ARCH-2151

Summary by CodeRabbit

  • Refactor
    • Introduced a shared "MeteorErrorLike" shape for authentication and 2FA error typing.
    • Updated 2FA utilities and flows to rely on the shared error shape and narrowed exported signatures.
  • Behavior
    • Standardized cancellation and verification failures to reject/throw plain Error instances (with cause).
  • Chore
    • Removed a legacy reactive subscription factory and eliminated direct Meteor error imports.
  • Init
    • Added an extra client override to the startup load sequence.

Review Change Stack

@ggazzo
Copy link
Copy Markdown
Member Author

ggazzo commented May 15, 2026

/jira ARCH-2116

@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented May 15, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@ggazzo ggazzo added this to the 8.5.0 milestone May 15, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a shared MeteorErrorLike structural type and updates 2FA utilities, login/2FA flow typings, error construction (use standard Error), removes a reactive subscription factory, and adjusts client startup/override imports.

Changes

Meteor Error Type Abstraction in 2FA and Account Flows

Layer / File(s) Summary
MeteorErrorLike type definition
apps/meteor/client/lib/2fa/types.ts
New exported MeteorErrorLike structural type extends Error with optional error, errorType, reason, and details.
2FA error type guards and utilities
apps/meteor/client/lib/2fa/utils.ts
Type-guard functions (isTotpRequiredError, isTotpInvalidError, isTotpMaxAttemptsError) now narrow to MeteorErrorLike & ... instead of Meteor.Error & ..., preserving runtime checks.
Login and 2FA flow type updates
apps/meteor/client/lib/2fa/overrideLoginMethod.ts, apps/meteor/client/lib/2fa/process2faReturn.ts
LoginCallback error type, hasRequiredTwoFactorMethod type guard, and process2faReturn parameter types updated to use MeteorErrorLike; imports adjusted (type-only imports and removal of Meteor where applicable); callback and rejection handler parameter annotations updated.
Error construction refactoring
apps/meteor/client/lib/2fa/process2faReturn.ts, apps/meteor/client/meteor/startup/accounts.ts
2FA modal cancellation and email verification failure now throw standard Error instances (email verification attaches original error as cause) instead of Meteor.Error.
Module initialization
apps/meteor/client/meteor/overrides/index.ts, apps/meteor/client/meteor/startup/index.ts
Added ./absoluteUrl side-effect import to overrides and adjusted startup imports to include accounts.
Removed reactive subscription factory
apps/meteor/client/lib/createReactiveSubscriptionFactory.ts
Deletes createReactiveSubscriptionFactory and its ISubscriptionFactory interface, removing Tracker-based reactive subscription helper.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

area: authentication

Suggested reviewers

  • tassoevan
  • MartinSchoeler
  • gabriellsh
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: removing unnecessary Meteor type/value imports from 2FA and startup modules by introducing a local MeteorErrorLike type and other refactoring.
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.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • ARCH-2151: Request failed with status code 401

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.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 15, 2026

⚠️ No Changeset found

Latest commit: 8a66cbb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 28.57143% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.65%. Comparing base (18031de) to head (8a66cbb).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #40556      +/-   ##
===========================================
+ Coverage    69.64%   69.65%   +0.01%     
===========================================
  Files         3325     3320       -5     
  Lines       122799   122709      -90     
  Branches     21880    21899      +19     
===========================================
- Hits         85519    85477      -42     
+ Misses       33942    33887      -55     
- Partials      3338     3345       +7     
Flag Coverage Δ
e2e 59.28% <28.57%> (+0.07%) ⬆️
e2e-api 46.24% <ø> (-0.07%) ⬇️
unit 70.33% <ø> (-0.01%) ⬇️

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

🚀 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.

@ggazzo ggazzo marked this pull request as ready for review May 15, 2026 12:58
@ggazzo ggazzo requested a review from a team as a code owner May 15, 2026 12:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/meteor/client/lib/2fa/utils.ts (1)

10-14: ⚡ Quick win

Add object check for consistent type guard validation.

isTotpInvalidError and isTotpMaxAttemptsError don't validate that error is an object before narrowing to MeteorErrorLike (which extends Error). While runtime-safe due to optional chaining, this creates a type safety gap and is inconsistent with isTotpRequiredError (line 6). For correctness and consistency, add the typeof error === 'object' check.

🔒 Proposed fix to add object validation
 export const isTotpInvalidError = (
 	error: unknown,
 ): error is MeteorErrorLike & ({ error: 'totp-invalid' } | { errorType: 'totp-invalid' }) =>
+	typeof error === 'object' &&
+	(
 	(error as { error?: unknown } | undefined)?.error === 'totp-invalid' ||
-	(error as { errorType?: unknown } | undefined)?.errorType === 'totp-invalid';
+	(error as { errorType?: unknown } | undefined)?.errorType === 'totp-invalid'
+	);

 export const isTotpMaxAttemptsError = (
 	error: unknown,
 ): error is MeteorErrorLike & ({ error: 'totp-max-attempts' } | { errorType: 'totp-max-attempts' }) =>
+	typeof error === 'object' &&
+	(
 	(error as { error?: unknown } | undefined)?.error === 'totp-max-attempts' ||
-	(error as { errorType?: unknown } | undefined)?.errorType === 'totp-max-attempts';
+	(error as { errorType?: unknown } | undefined)?.errorType === 'totp-max-attempts'
+	);

Also applies to: 16-20

🤖 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/meteor/client/lib/2fa/utils.ts` around lines 10 - 14, isTotpInvalidError
(and similarly isTotpMaxAttemptsError) currently narrows error without verifying
it's an object; update both type guards to first check typeof error === 'object'
(and error !== null) before checking .error or .errorType so the runtime check
matches isTotpRequiredError and safely narrows to MeteorErrorLike; locate the
functions by name (isTotpInvalidError, isTotpMaxAttemptsError) and add the
object-type guard to the existing boolean expression.
🤖 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/meteor/client/lib/2fa/process2faReturn.ts`:
- Line 161: The rejection currently uses reject(new Error('totp-canceled'))
which lacks the .error property expected by downstream code (LoginForm's
onError). Change the rejection to produce a Meteor.Error with the same code
(e.g. use new Meteor.Error('totp-canceled')) or, if you must keep a plain Error,
attach an .error = 'totp-canceled' property before rejecting in
process2faReturn.ts so that the downstream error.error lookup continues to work.

---

Nitpick comments:
In `@apps/meteor/client/lib/2fa/utils.ts`:
- Around line 10-14: isTotpInvalidError (and similarly isTotpMaxAttemptsError)
currently narrows error without verifying it's an object; update both type
guards to first check typeof error === 'object' (and error !== null) before
checking .error or .errorType so the runtime check matches isTotpRequiredError
and safely narrows to MeteorErrorLike; locate the functions by name
(isTotpInvalidError, isTotpMaxAttemptsError) and add the object-type guard to
the existing boolean expression.
🪄 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: 597bc40d-2d75-4c1d-a3d7-de31c6a2d599

📥 Commits

Reviewing files that changed from the base of the PR and between 390a7f4 and bad4407.

📒 Files selected for processing (8)
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/process2faReturn.ts
  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/lib/2fa/utils.ts
  • apps/meteor/client/meteor/overrides/absoluteUrl.ts
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/meteor/startup/index.ts
💤 Files with no reviewable changes (1)
  • apps/meteor/client/meteor/startup/index.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (2/5)
  • GitHub Check: 🔨 Test API (EE) / MongoDB 8.0 coverage (1/1)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (1/5)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (3/5)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (4/5)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (3/4)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (5/5)
  • GitHub Check: 🔨 Test API Livechat (EE) / MongoDB 8.0 coverage (1/1)
  • GitHub Check: 🔨 Test API Livechat (CE) / MongoDB 8.0 (1/1)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (4/4)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (2/4)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (1/4)
  • GitHub Check: 🔨 Test API (CE) / MongoDB 8.0 (1/1)
  • GitHub Check: 🔨 Test Federation Matrix
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/lib/2fa/process2faReturn.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/utils.ts
🧠 Learnings (5)
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.

Applied to files:

  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/lib/2fa/process2faReturn.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/utils.ts
📚 Learning: 2026-05-11T20:30:35.265Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 40480
File: apps/meteor/client/meteor/startup/accounts.ts:59-61
Timestamp: 2026-05-11T20:30:35.265Z
Learning: In Rocket.Chat’s Meteor client code, when calling `dispatchToastMessage` with `{ type: 'error' }`, pass the raw caught error object as `message` without manual normalization. `dispatchToastMessage` is designed to accept `message: unknown` for error toasts, so avoid converting errors to strings (e.g., `String(error)`) or extracting `error.message` before passing them.

Applied to files:

  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/lib/2fa/process2faReturn.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/utils.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/lib/2fa/process2faReturn.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/utils.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/lib/2fa/process2faReturn.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/utils.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/lib/2fa/process2faReturn.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/utils.ts
🔇 Additional comments (4)
apps/meteor/client/meteor/overrides/index.ts (1)

1-1: LGTM!

apps/meteor/client/lib/2fa/types.ts (1)

1-9: LGTM!

apps/meteor/client/lib/2fa/overrideLoginMethod.ts (1)

1-8: LGTM!

apps/meteor/client/meteor/startup/accounts.ts (1)

58-58: Verify that downstream code doesn't rely on Meteor.Error fields.

The error construction changed from new Meteor.Error('verify-email', 'E-mail not verified', error) to new Error('verify-email: E-mail not verified', { cause: error }). This removes the error, errorType, reason, and details fields that Meteor.Error provides.

A codebase-wide search found no downstream handlers checking for error.error === 'verify-email' or accessing these structured fields for this specific error. The error is used only to display a toast message (which uses the Error's message property) and then rethrown—no upstream catch handler was found that would need the Meteor.Error structure. The change is safe.

});
});
reject(new Meteor.Error('totp-canceled'));
reject(new Error('totp-canceled'));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for code that might check for 'totp-canceled' in error fields
rg -nP --type=ts --type=js -C3 '\b(error|errorType)\b.*totp-canceled|totp-canceled.*\b(error|errorType)\b'

Repository: RocketChat/Rocket.Chat

Length of output: 490


🏁 Script executed:

# Check the context around the LoginForm.tsx error comparison
rg -n -B5 -A5 "if \(error === 'totp-canceled'\)" packages/web-ui-registration/src/LoginForm.tsx

Repository: RocketChat/Rocket.Chat

Length of output: 236


🏁 Script executed:

# Search for calls to process2faReturn to understand error handling
rg -n "process2faReturn" --type=ts --type=js -A2 -B2

Repository: RocketChat/Rocket.Chat

Length of output: 3448


🏁 Script executed:

# Search for openTwoFactorModal and error handling
rg -n "openTwoFactorModal" --type=ts --type=js -B3 -A3 | head -50

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

# Search for where 'totp-canceled' string is used as error message
rg -n "totp-canceled" --type=ts --type=js

Repository: RocketChat/Rocket.Chat

Length of output: 382


🏁 Script executed:

# Check the process2faReturn function signature and return values
rg -n "export.*process2faReturn|reject.*totp-canceled|resolve" apps/meteor/client/lib/2fa/process2faReturn.ts -B2 -A2

Repository: RocketChat/Rocket.Chat

Length of output: 564


🏁 Script executed:

# Find where the error from process2faReturn is caught/handled
rg -n "process2faReturn\(" --type=ts --type=js -A10 | head -80

Repository: RocketChat/Rocket.Chat

Length of output: 3446


🏁 Script executed:

# Check the openTwoFactorModal function and how it handles errors
rg -n "openTwoFactorModal|function openTwoFactorModal" --type=ts --type=js -B3 -A15 | head -100

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

# Find the complete process2faReturn function to see all paths
cat -n apps/meteor/client/lib/2fa/process2faReturn.ts

Repository: RocketChat/Rocket.Chat

Length of output: 6039


🏁 Script executed:

# Search for where LoginForm is used and where the 'error' prop comes from
rg -n "LoginForm" --type=ts --type=tsx -B3 -A3 | head -100

Repository: RocketChat/Rocket.Chat

Length of output: 94


🏁 Script executed:

# Search for modal/dialog showing with error handling
rg -n "imperativeModal\|openModal" apps/meteor/client/lib/2fa/process2faReturn.ts -B5 -A5

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

# Find where LoginForm is used with the error prop
rg -n "LoginForm" packages/web-ui-registration -B5 -A5 --type=tsx

Repository: RocketChat/Rocket.Chat

Length of output: 94


🏁 Script executed:

# Search for error handling around the LoginForm usage context
rg -n "error === 'totp-canceled'|LoginErrors" packages/web-ui-registration/src/LoginForm.tsx -B10 -A2

Repository: RocketChat/Rocket.Chat

Length of output: 1273


🏁 Script executed:

# Find what calls the modal and how it catches the error
rg -n "invokeTwoFactorModal\|TwoFactorModal" --type=ts --type=tsx -B5 -A15 | head -150

Repository: RocketChat/Rocket.Chat

Length of output: 94


🏁 Script executed:

# Search for where the rejected error is caught and handled
rg -n "catch|\.message|Error.*totp-canceled" apps/meteor/client/lib/2fa/ --type=ts -B3 -A3

Repository: RocketChat/Rocket.Chat

Length of output: 2202


🏁 Script executed:

# Find files that catch the invokeTwoFactorModal promise and how they handle it
rg -n "invokeTwoFactorModal" apps/meteor/client -B2 -A10 --type=ts

Repository: RocketChat/Rocket.Chat

Length of output: 2610


🏁 Script executed:

# Look for error.message usage related to 2fa
rg -n "error\.message|error\.toString" apps/meteor/client/lib/2fa/ --type=ts

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

# Check the LoginError type definition
rg -n "type LoginError|interface LoginError" apps/meteor/client --type=ts -B2 -A5

Repository: RocketChat/Rocket.Chat

Length of output: 828


🏁 Script executed:

# Look at the callback function handling in overrideLoginMethod more carefully
cat -n apps/meteor/client/lib/2fa/overrideLoginMethod.ts | head -100

Repository: RocketChat/Rocket.Chat

Length of output: 4124


🏁 Script executed:

# Check what happens when the error is passed to callback
rg -n "callback.*error|setErrorOnSubmit" apps/meteor/client --type=ts -B3 -A3 | head -80

Repository: RocketChat/Rocket.Chat

Length of output: 6110


🏁 Script executed:

# Check if there's any conversion of Error to string before reaching LoginForm
rg -n "setErrorOnSubmit|error\.message|String.*error|\.toString" packages/web-ui-registration/src/LoginForm.tsx --type=ts -B3 -A3

Repository: RocketChat/Rocket.Chat

Length of output: 733


🏁 Script executed:

# Search for how the error object flows through the authentication provider
rg -n "setErrorOnSubmit|LoginErrors" packages/web-ui-registration -B5 -A5 --type=ts

Repository: RocketChat/Rocket.Chat

Length of output: 6705


🏁 Script executed:

# Check what MeteorErrorLike type definition looks like
cat -n apps/meteor/client/lib/2fa/types.ts

Repository: RocketChat/Rocket.Chat

Length of output: 486


🏁 Script executed:

# Verify that the error being set in errorOnSubmit is indeed error.error from the caught error
cat -n packages/web-ui-registration/src/LoginForm.tsx | sed -n '100,145p'

Repository: RocketChat/Rocket.Chat

Length of output: 1641


🏁 Script executed:

# Check if there's any transformation of the Error object before it reaches LoginForm
rg -n "useLoginWithPassword|loginMutation" packages/web-ui-registration/src/LoginForm.tsx -B3 -A8

Repository: RocketChat/Rocket.Chat

Length of output: 3376


Plain Error lacks the .error property that downstream code depends on.

Changing from new Meteor.Error('totp-canceled') to new Error('totp-canceled') breaks downstream error handling. The LoginForm component's onError handler (packages/web-ui-registration/src/LoginForm.tsx, lines 101-115) explicitly accesses error.error to extract the error code and compare it against known error types. A plain Error has no .error property, so the value will be undefined instead of 'totp-canceled', causing the error check at line 140 to fail. Use new Meteor.Error('totp-canceled') or add an .error property to the plain Error object to preserve the expected behavior.

🤖 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/meteor/client/lib/2fa/process2faReturn.ts` at line 161, The rejection
currently uses reject(new Error('totp-canceled')) which lacks the .error
property expected by downstream code (LoginForm's onError). Change the rejection
to produce a Meteor.Error with the same code (e.g. use new
Meteor.Error('totp-canceled')) or, if you must keep a plain Error, attach an
.error = 'totp-canceled' property before rejecting in process2faReturn.ts so
that the downstream error.error lookup continues to work.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 8 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/meteor/client/lib/2fa/process2faReturn.ts">

<violation number="1" location="apps/meteor/client/lib/2fa/process2faReturn.ts:161">
P1: `new Error('totp-canceled')` lacks the `.error` property that downstream code depends on. `Meteor.Error` stores its first argument as the `.error` property (i.e., `err.error === 'totp-canceled'`), while a plain `Error` only sets `.message`. The LoginForm `onError` handler reads `error.error` to detect `'totp-canceled'` and suppress the error display — this will now always be `undefined`, breaking the cancellation flow.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

});
});
reject(new Meteor.Error('totp-canceled'));
reject(new Error('totp-canceled'));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: new Error('totp-canceled') lacks the .error property that downstream code depends on. Meteor.Error stores its first argument as the .error property (i.e., err.error === 'totp-canceled'), while a plain Error only sets .message. The LoginForm onError handler reads error.error to detect 'totp-canceled' and suppress the error display — this will now always be undefined, breaking the cancellation flow.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/lib/2fa/process2faReturn.ts, line 161:

<comment>`new Error('totp-canceled')` lacks the `.error` property that downstream code depends on. `Meteor.Error` stores its first argument as the `.error` property (i.e., `err.error === 'totp-canceled'`), while a plain `Error` only sets `.message`. The LoginForm `onError` handler reads `error.error` to detect `'totp-canceled'` and suppress the error display — this will now always be `undefined`, breaking the cancellation flow.</comment>

<file context>
@@ -158,7 +158,7 @@ export const invokeTwoFactorModal = async (
 							});
 						});
-						reject(new Meteor.Error('totp-canceled'));
+						reject(new Error('totp-canceled'));
 					}
 				},
</file context>

ggazzo added 3 commits May 18, 2026 15:47
Three small cleanups that don't fit any larger cluster:

- lib/2fa/utils.ts only imported `import type { Meteor }` to spell
  `Meteor.Error` inside type-guard return types. The guards themselves
  check `.error` / `.errorType` structurally — they never instanceof —
  so a local `MeteorErrorLike = Error & { error?; errorType?; reason?;
  details? }` type covers both `Meteor.Error` and `Meteor.TypedError`
  shapes without pulling `meteor/meteor` for types.
- lib/2fa/overrideLoginMethod.ts switches its `LoginError` alias from
  `globalThis.Error | Meteor.Error | Meteor.TypedError` to
  `MeteorErrorLike` for the same reason. The `Accounts` value import
  stays — `Accounts.callLoginMethod` migrates separately.
- lib/2fa/process2faReturn.ts narrows `hasRequiredTwoFactorMethod` and
  the public function signatures to `MeteorErrorLike` so the new local
  type composes through. The `Meteor` value import stays for the runtime
  `new Meteor.Error('totp-canceled')` thrown when the 2FA modal closes
  — replacing that requires auditing the catch sites and is out of scope.
- meteor/startup/absoluteUrl.ts patches `Meteor.absoluteUrl` /
  `Meteor._relativeToSiteRootUrl` on the client globals. Zero callers
  read either on the client (verified by grep — every consumer is
  server-side, where Meteor's own absoluteUrl runs). The file already
  carried a TODO marking itself as dead once DDP is gone. Delete it.
The Meteor.absoluteUrl / Meteor._relativeToSiteRootUrl patch lives until
Meteor's DDP layer goes away. Park it under meteor/overrides/ alongside
the other DDP-layer patches so the whole folder can exit together.

Same content, same load semantics — overrides/index already runs at
startup via main.ts.
…erify

Two runtime `new Meteor.Error(code, reason)` callsites in non-OAuth /
non-overrides client code. Neither is `instanceof`-checked anywhere — the
only `instanceof Meteor.Error` in the client lives in OAuth login flow,
out of scope here — and neither error code is read by any consumer:

- meteor/startup/accounts.ts throws 'verify-email' from inside the
  email-verification onLink handler's catch. The throw propagates as an
  unhandled rejection (the handler is fire-and-forget). Replace with
  `new Error('verify-email: E-mail not verified', { cause: error })` so
  the original failure stays attached for logging.
- lib/2fa/process2faReturn.ts rejects with 'totp-canceled' when the 2FA
  modal closes without a confirm. The reject bubbles into the
  process2faAsyncReturn / process2faReturn callers, which only forward
  via callback — no consumer reads `.error`. Plain `new Error('totp-canceled')`
  keeps the rejection identifiable by message.

Drops the `Meteor` value import from both files. lib/2fa/ is now
Meteor-free.
@ggazzo ggazzo force-pushed the refactor/drop-misc-meteor-imports branch from d5703e2 to 60aeb6c Compare May 18, 2026 18:49
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
apps/meteor/client/lib/2fa/process2faReturn.ts (1)

19-20: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate every field this type guard narrows.

The predicate says details.emailOrUsername is string | undefined, but it only checks method. In process2faAsyncReturn, a truthy non-string value will bypass the fallback and then fail assertModalProps, turning a 2FA flow into Invalid Two Factor method. Either validate emailOrUsername here or remove it from the narrowed type.

Proposed fix
 const hasRequiredTwoFactorMethod = (
 	error: MeteorErrorLike,
 ): error is MeteorErrorLike & { details: { method: TwoFactorMethod; emailOrUsername?: string } } => {
 	const details = error.details as unknown;
 
 	return (
 		typeof details === 'object' &&
 		details !== null &&
 		typeof (details as { method: unknown }).method === 'string' &&
+		((details as { emailOrUsername?: unknown }).emailOrUsername === undefined ||
+			typeof (details as { emailOrUsername?: unknown }).emailOrUsername === 'string') &&
 		isTwoFactorMethod((details as { method: string }).method)
 	);
 };
🤖 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/meteor/client/lib/2fa/process2faReturn.ts` around lines 19 - 20, The
type guard currently narrows error.details to include emailOrUsername but only
checks method; update the predicate (the arrow function that takes error:
MeteorErrorLike) to also validate error.details.emailOrUsername (ensure
error.details exists and that emailOrUsername is either undefined or a string
via a typeof check) or else remove emailOrUsername from the narrowed type; make
this change where the type guard is defined so callers like
process2faAsyncReturn and assertModalProps cannot receive a falsely-narrowed
details object.
🤖 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.

Duplicate comments:
In `@apps/meteor/client/lib/2fa/process2faReturn.ts`:
- Around line 19-20: The type guard currently narrows error.details to include
emailOrUsername but only checks method; update the predicate (the arrow function
that takes error: MeteorErrorLike) to also validate
error.details.emailOrUsername (ensure error.details exists and that
emailOrUsername is either undefined or a string via a typeof check) or else
remove emailOrUsername from the narrowed type; make this change where the type
guard is defined so callers like process2faAsyncReturn and assertModalProps
cannot receive a falsely-narrowed details object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7ed7963-8afa-43f3-b79a-43f9ae52aeb9

📥 Commits

Reviewing files that changed from the base of the PR and between d5703e2 and 60aeb6c.

📒 Files selected for processing (8)
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/process2faReturn.ts
  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/lib/2fa/utils.ts
  • apps/meteor/client/meteor/overrides/absoluteUrl.ts
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/meteor/startup/index.ts
💤 Files with no reviewable changes (1)
  • apps/meteor/client/meteor/startup/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/meteor/client/meteor/overrides/index.ts
  • apps/meteor/client/lib/2fa/overrideLoginMethod.ts
  • apps/meteor/client/lib/2fa/types.ts
  • apps/meteor/client/meteor/startup/accounts.ts
  • apps/meteor/client/lib/2fa/utils.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/lib/2fa/process2faReturn.ts
🧠 Learnings (5)
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.

Applied to files:

  • apps/meteor/client/lib/2fa/process2faReturn.ts
📚 Learning: 2026-05-11T20:30:35.265Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 40480
File: apps/meteor/client/meteor/startup/accounts.ts:59-61
Timestamp: 2026-05-11T20:30:35.265Z
Learning: In Rocket.Chat’s Meteor client code, when calling `dispatchToastMessage` with `{ type: 'error' }`, pass the raw caught error object as `message` without manual normalization. `dispatchToastMessage` is designed to accept `message: unknown` for error toasts, so avoid converting errors to strings (e.g., `String(error)`) or extracting `error.message` before passing them.

Applied to files:

  • apps/meteor/client/lib/2fa/process2faReturn.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/client/lib/2fa/process2faReturn.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/client/lib/2fa/process2faReturn.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • apps/meteor/client/lib/2fa/process2faReturn.ts
🔇 Additional comments (2)
apps/meteor/client/lib/2fa/process2faReturn.ts (2)

161-161: totp-canceled still needs the Meteor-style error code.

This is the same downstream contract issue already called out earlier: a plain Error still drops the .error field that login error handling reads.


6-6: LGTM!

Also applies to: 64-64

Comment thread apps/meteor/client/lib/2fa/overrideLoginMethod.ts Outdated
The three former consumers (useReactiveValue, AuthorizationProvider,
UserProvider) all switched to native useSyncExternalStore wiring across
prior PRs. No remaining importers — drop the file (and the last
non-overrides meteor/tracker import outside Minimongo).
Drop redundant `LoginError = MeteorErrorLike` alias per review.
@ggazzo
Copy link
Copy Markdown
Member Author

ggazzo commented May 18, 2026

@cubic-dev-ai about the totp-canceled reject: verified there are no consumers reading .error on this specific rejection — grep -rn "totp-canceled" apps/meteor (excluding node_modules + specs) returns only the throwing line itself. The reject bubbles up through process2faReturn / process2faAsyncReturn to login flows that just forward the error object to the callback or rethrow; no code path branches on err.error === 'totp-canceled'. The shape change (string in .message instead of .error) is invisible to callers.

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented May 18, 2026

@cubic-dev-ai about the totp-canceled reject: verified there are no consumers reading .error on this specific rejection — grep -rn "totp-canceled" apps/meteor (excluding node_modules + specs) returns only the throwing line itself. The reject bubbles up through process2faReturn / process2faAsyncReturn to login flows that just forward the error object to the callback or rethrow; no code path branches on err.error === 'totp-canceled'. The shape change (string in .message instead of .error) is invisible to callers.

@ggazzo Yep, that matches what the code shows. The totp-canceled error flows through process2faReturn → callers of invokeTwoFactorModal → either a callback or a rethrow, and none of those paths inspect .error or .errorType. Since the rejection is identified solely by the .message now, the change is transparent to every consumer.

The grep confirming zero other references seals it — there's no hidden instanceof Meteor.Error check or .error read elsewhere that could break. Clean analysis.

SDKClient.ts:406 referenced the now-removed DDPCommon namespace inside
the sdk-transport-on branch added by #40542. The DDPCommon import was
dropped in #40486 when the codec moved behind sdk/ddpProtocol; the
follow-up landed without rebasing onto that, so TS now blows up:

  TS2708: Cannot use namespace 'DDPCommon' as a value.

Same parseDDP helper is already used a few lines up — just call it.
await whenMainReady();
dispatchToastMessage({ type: 'error', message: error });
throw new Meteor.Error('verify-email', 'E-mail not verified');
throw new Error('verify-email: E-mail not verified', { cause: error });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why not a custom Error subclass?

@ggazzo ggazzo merged commit 0673099 into develop May 18, 2026
43 of 47 checks passed
@ggazzo ggazzo deleted the refactor/drop-misc-meteor-imports branch May 18, 2026 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants