Skip to content

Comments

refactor: move 2FA disable logic to Authorization service#38781

Open
Saraabodeeb wants to merge 5 commits intoRocketChat:developfrom
Saraabodeeb:refactor/move-2fa-atomic-clean
Open

refactor: move 2FA disable logic to Authorization service#38781
Saraabodeeb wants to merge 5 commits intoRocketChat:developfrom
Saraabodeeb:refactor/move-2fa-atomic-clean

Conversation

@Saraabodeeb
Copy link

@Saraabodeeb Saraabodeeb commented Feb 18, 2026

Summary
Following the feedback to make PRs more atomic and focused, this PR refactors the 2fa:disable logic. It moves the core functionality from the Meteor methods to the Authorization service within core-services.

Changes
Interface Update: Added disable2FA(uid: string, code: string) to the IAuthorization interface in packages/core-services.

Service Implementation: Implemented the disable2FA logic in Authorization service.

Added security verification using TOTP.verify to ensure the 2FA code is valid before disabling.

Used Users.disable2FAByUserId(uid) for database updates.

Method Refactoring: Updated the 2fa:disable Meteor method in apps/meteor to act as a proxy, delegating the core logic to the Authorization service.

Testing: Added a basic unit test in packages/core-services/tests/Authorization.spec.ts to verify the method definition.

Security Note
[!IMPORTANT]
As discussed in previous reviews, security is a priority. The service now handles the verification step internally before performing any database operations to prevent unauthorized 2FA deactivation.

Summary by CodeRabbit

  • Refactor
    • Centralized the two-factor authentication (2FA) disable flow into the authorization service for improved consistency and maintainability.
  • Tests
    • Added comprehensive tests covering invalid users, already-disabled 2FA, invalid codes, and successful 2FA disable scenarios.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Feb 18, 2026

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

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Feb 18, 2026

⚠️ No Changeset found

Latest commit: 45b13d0

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Walkthrough

Refactors the 2FA disable flow by moving verification, DB update, and notification logic into Authorization.disable2FA(uid, code); the Meteor method now verifies authentication and delegates to that service.

Changes

Cohort / File(s) Summary
Authorization Service & Interface
apps/meteor/server/services/authorization/service.ts, packages/core-services/src/types/IAuthorization.ts
Added disable2FA(uid: string, code: string): Promise<boolean> to Authorization/IAuthorization. Validates user, verifies TOTP (including backups), disables 2FA via Users.disable2FAByUserId, and triggers a user-change notification on success.
Meteor 2FA Method
apps/meteor/app/2fa/server/methods/disable.ts
Replaced inline multi-step disable implementation with a delegate call to Authorization.disable2FA(userId, code) after auth check; removed direct TOTP, Users model, and notification logic.
Authorization Service Tests
apps/meteor/server/services/authorization/service.spec.ts
Added tests covering invalid user, 2FA already disabled, invalid TOTP, and successful disable (including DB update assertion and notifyOnUserChange invocation).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant MeteorMethod as "Meteor '2fa:disable'"
  participant AuthService as "Authorization.disable2FA"
  participant TOTP
  participant UsersDB as "Users DB"
  participant Notifier as "notifyOnUserChange"

  Client->>MeteorMethod: call '2fa:disable'(code)
  MeteorMethod->>AuthService: disable2FA(userId, code)
  AuthService->>UsersDB: find user by uid
  UsersDB-->>AuthService: user record
  AuthService->>TOTP: verify(code, user.services.totp.secret, backups)
  TOTP-->>AuthService: valid / invalid
  alt valid
    AuthService->>UsersDB: Users.disable2FAByUserId(uid)
    UsersDB-->>AuthService: { modifiedCount: 1 }
    AuthService->>Notifier: notifyOnUserChange({...diff: {'services.totp.enabled': false} })
    Notifier-->>AuthService: ack
    AuthService-->>MeteorMethod: true
    MeteorMethod-->>Client: true
  else invalid
    AuthService-->>MeteorMethod: throw error / false
    MeteorMethod-->>Client: error / false
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped to tidy the auth-lined trail,
One call to rule the TOTP tale,
Secrets checked, backups peeked,
Flags flipped low, notifications tweaked,
A little rabbit hops off, light and hale ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 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: moving 2FA disable logic from Meteor methods into the Authorization service, which is the core refactoring objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


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.

@KevLehman KevLehman changed the title Refactor: Move 2FA disable logic to Authorization service refactor: Move 2FA disable logic to Authorization service Feb 18, 2026
@Saraabodeeb Saraabodeeb changed the title refactor: Move 2FA disable logic to Authorization service refactor: move 2FA disable logic to Authorization service Feb 18, 2026
@Saraabodeeb Saraabodeeb marked this pull request as ready for review February 18, 2026 19:52
@Saraabodeeb Saraabodeeb requested a review from a team as a code owner February 18, 2026 19:52
Copy link
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.

No issues found across 4 files

Copy link
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: 4

🧹 Nitpick comments (1)
apps/meteor/server/services/authorization/service.ts (1)

206-221: disable2FA returns false for two unrelated failure modes — callers cannot distinguish them.

Returning false for both "user has no TOTP enabled" (line 208) and "TOTP verification failed" (line 217) collapses two semantically distinct outcomes into a single falsy signal. The Meteor method (and any future callers) cannot provide meaningful feedback to the user — e.g., the difference between "2FA is not active on your account" and "the code you entered is wrong."

Consider returning a discriminated type or throwing distinct Meteor.Error/service errors for each path:

♻️ Suggested approach — throw on verification failure
 	async disable2FA(uid: string, code: string): Promise<boolean> {
 		const user = await Users.findOneById(uid, { projection: { 'services.totp.enabled': 1, 'services.totp.secret': 1, 'services.totp.hashedBackup': 1 } });
 		if (!user?.services?.totp?.enabled) return false;

 		const verified = await TOTP.verify({
 			secret: user.services.totp.secret,
 			token: code,
 			userId: uid,
 			backupTokens: user.services.totp.hashedBackup,
 		});

-		if (!verified) return false;
+		if (!verified) throw new Error('totp-invalid');

 		const { modifiedCount } = await Users.disable2FAByUserId(uid);
 		return modifiedCount > 0;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/authorization/service.ts` around lines 206 - 221,
The disable2FA function currently returns false for both "TOTP not enabled" and
"verification failed", making callers unable to distinguish the cases; update
disable2FA to throw distinct errors instead of returning false for the two
failure modes (use Meteor.Error or your service error class): when
user?.services?.totp?.enabled is falsy, throw a clear error (e.g., code
"2fa-not-enabled", message "Two-factor authentication is not enabled for this
account"), and when TOTP.verify returns false throw a different error (e.g.,
code "invalid-2fa-token", message "Invalid two-factor authentication code");
keep the call to TOTP.verify and Users.disable2FAByUserId and return true on
successful modifiedCount > 0 (or throw if DB update fails) so callers can
distinguish all outcomes.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fef5ac and 18ba7a5.

📒 Files selected for processing (4)
  • apps/meteor/app/2fa/server/methods/disable.ts
  • apps/meteor/server/services/authorization/service.ts
  • packages/core-services/src/types/IAuthorization.ts
  • packages/core-services/tests/Authorization.spec.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{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:

  • packages/core-services/src/types/IAuthorization.ts
  • packages/core-services/tests/Authorization.spec.ts
  • apps/meteor/server/services/authorization/service.ts
  • apps/meteor/app/2fa/server/methods/disable.ts
**/*.spec.ts

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

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • packages/core-services/tests/Authorization.spec.ts
🧠 Learnings (9)
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • packages/core-services/src/types/IAuthorization.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings by mapping subscription documents to room IDs, never undefined, even when user has no room subscriptions.

Applied to files:

  • packages/core-services/src/types/IAuthorization.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Group related tests in the same file

Applied to files:

  • packages/core-services/tests/Authorization.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • packages/core-services/tests/Authorization.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • packages/core-services/tests/Authorization.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to **/*.spec.ts : Use `.spec.ts` extension for test files (e.g., `login.spec.ts`)

Applied to files:

  • packages/core-services/tests/Authorization.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • packages/core-services/tests/Authorization.spec.ts
📚 Learning: 2025-12-10T21:00:54.909Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37091
File: ee/packages/abac/jest.config.ts:4-7
Timestamp: 2025-12-10T21:00:54.909Z
Learning: Rocket.Chat monorepo: Jest testMatch pattern '<rootDir>/src/**/*.spec.(ts|js|mjs)' is valid in this repo and used across multiple packages (e.g., packages/tools, ee/packages/omnichannel-services). Do not flag it as invalid in future reviews.

Applied to files:

  • packages/core-services/tests/Authorization.spec.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/app/2fa/server/methods/disable.ts
🧬 Code graph analysis (4)
packages/core-services/src/types/IAuthorization.ts (1)
apps/meteor/app/2fa/server/methods/disable.ts (1)
  • code (6-20)
packages/core-services/tests/Authorization.spec.ts (1)
packages/core-services/src/types/IAuthorization.ts (1)
  • IAuthorization (9-19)
apps/meteor/server/services/authorization/service.ts (2)
apps/meteor/app/2fa/server/methods/disable.ts (1)
  • code (6-20)
apps/meteor/app/authentication/server/startup/index.js (1)
  • verified (222-222)
apps/meteor/app/2fa/server/methods/disable.ts (1)
apps/meteor/app/lib/server/lib/notifyListener.ts (1)
  • notifyOnUserChange (377-389)
⏰ 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). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (3)
packages/core-services/src/types/IAuthorization.ts (1)

18-18: LGTM — interface contract is clean and consistent.

The signature aligns with the implementation and the Promise<boolean> return type is appropriate for the contract.

packages/core-services/tests/Authorization.spec.ts (1)

1-8: No action required. The Jest configuration for packages/core-services uses the server preset without defining a restrictive testMatch pattern, which means it relies on Jest's default discovery patterns including **/?(*.)+(spec|test).[jt]s?(x). The test file at tests/Authorization.spec.ts matches this pattern and will be discovered by Jest.

Likely an incorrect or invalid review comment.

apps/meteor/server/services/authorization/service.ts (1)

208-215: ⚠️ Potential issue | 🟡 Minor

Add defensive null checks for secret and hashedBackup after the enabled check.

The optional chaining guard on line 208 doesn't provide TypeScript type narrowing. While the type definition declares secret and hashedBackup as required fields within the totp object, after the guard !user?.services?.totp?.enabled, TypeScript cannot guarantee these fields exist. The ?. operator evaluates but doesn't narrow types. If the TOTP object is partially initialized or corrupted in the database, TOTP.verify could receive undefined values.

For consistency and safety, add explicit null checks for secret before accessing it (as done in apps/meteor/app/2fa/server/code/TOTPCheck.ts:23).

⛔ Skipped due to learnings
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 38227
File: apps/meteor/app/api/server/router.ts:44-49
Timestamp: 2026-01-26T18:26:01.279Z
Learning: In apps/meteor/app/api/server/router.ts, when retrieving bodyParams and queryParams from the Hono context via c.get(), do not add defensive defaults (e.g., ?? {}). The code should fail fast if these parameters are missing, as endpoint handlers expect them to be present and breaking here helps surface parsing problems rather than hiding them.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/meteor/app/2fa/server/methods/disable.ts`:
- Around line 5-21: Add a TypeScript declaration augmenting module
'@rocket.chat/ddp-client' with interface ServerMethods that includes
"'2fa:disable'(code: string): Promise<boolean>;" and then apply that generic to
the Meteor.methods call (i.e., Meteor.methods<ServerMethods>({...})), keeping
the existing async '2fa:disable' implementation intact; this will provide proper
typing for useMethod('2fa:disable') in client code and match the pattern used by
other 2fa methods.
- Around line 10-19: Change Authorization.disable2FA to follow the existing 2FA
error pattern by throwing a distinguishable Meteor.Error (e.g., 'invalid-totp')
when verification fails instead of returning false, and keep returning/throwing
other errors for DB/update failures so callers can distinguish reasons; update
the method usage in disable.ts (the call to Authorization.disable2FA and
surrounding logic) to expect/propagate that error rather than treating all
falses the same, and add a ServerMethods interface declaration in this file
matching the pattern used in validateTempToken.ts / regenerateCodes.ts /
enable.ts to provide type safety for the RPC method signature.

In `@apps/meteor/server/services/authorization/service.ts`:
- Line 207: The Users.findOneById(uid) call in the authorization service loads
the entire user document; instead query only the TOTP fields used (e.g.
services.totp.*) by adding a projection to the findOneById call so only
services.totp.enabled and services.totp.secret (or services.totp.*) are
returned; update the call where Users.findOneById(uid) is invoked in the
authorization/service.ts function handling 2FA checks to include the projection
and adjust any downstream assumptions about missing fields accordingly.

In `@packages/core-services/tests/Authorization.spec.ts`:
- Around line 5-8: The current test is a tautology — remove the trivial expect
that compares the string literal and instead implement a runtime behavioral test
for disable2FA: call the concrete authorization implementation's disable2FA
method (use the service/class under test), mock dependencies like Users and TOTP
to simulate enabled/disabled states and valid/invalid codes, and assert the
expected outcomes (e.g., user 2FA flag cleared, errors thrown on invalid code).
If you only want a compile-time check, replace the test description to state it
is a TypeScript-only interface check and remove the runtime expect; otherwise
add mocks for Users and TOTP and assert concrete behavior of disable2FA. Ensure
references to IAuthorization and disable2FA remain so reviewers can locate the
change.

---

Nitpick comments:
In `@apps/meteor/server/services/authorization/service.ts`:
- Around line 206-221: The disable2FA function currently returns false for both
"TOTP not enabled" and "verification failed", making callers unable to
distinguish the cases; update disable2FA to throw distinct errors instead of
returning false for the two failure modes (use Meteor.Error or your service
error class): when user?.services?.totp?.enabled is falsy, throw a clear error
(e.g., code "2fa-not-enabled", message "Two-factor authentication is not enabled
for this account"), and when TOTP.verify returns false throw a different error
(e.g., code "invalid-2fa-token", message "Invalid two-factor authentication
code"); keep the call to TOTP.verify and Users.disable2FAByUserId and return
true on successful modifiedCount > 0 (or throw if DB update fails) so callers
can distinguish all outcomes.

@Saraabodeeb Saraabodeeb force-pushed the refactor/move-2fa-atomic-clean branch from 18ba7a5 to f136461 Compare February 20, 2026 21:36
@Saraabodeeb
Copy link
Author

Hi @KevLehman, I completely agree with testing the actual behavior! Since the 2fa:disable logic didn't have tests prior to this refactor, I want to make sure we set up the right testing foundation here.

To start, I've added a strict Behavioral Unit Test (apps/meteor/server/services/authorization/service.spec.ts) that uses calledOnceWithExactly to verify the exact parameters passed to DB operations and notifications, rather than just mocking return values.

I also want to add a proper Integration Test (inserting a real user into a Test DB and asserting the state change). However, to avoid MongoNotConnectedError in the CI, could you point me to the standard directory for DB-connected core service tests? (e.g., apps/meteor/tests/integration/...?).

Once you point me to the right suite, I’ll happily add the full integration test! Let me know what you think.

Copy link
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/server/services/authorization/service.spec.ts (1)

62-81: Add a test for the modifiedCount: 0 path after successful TOTP verification.

The success suite only covers modifiedCount: 1. There is a reachable code path in the implementation (lines 242–243 of service.ts) where TOTP verification passes but disable2FAByUserId returns { modifiedCount: 0 }, causing the method to return false without calling notifyOnUserChange. This path is currently untested.

➕ Suggested additional test case
+    it('should return false and not notify when DB update modifies nothing', async () => {
+        const testUid = 'valid_uid';
+        (Users.findOneById as sinon.SinonStub).resolves({
+            services: { totp: { enabled: true, secret: 'secret', hashedBackup: [] } },
+        });
+        (TOTP.verify as sinon.SinonStub).resolves(true);
+        (Users.disable2FAByUserId as sinon.SinonStub).resolves({ modifiedCount: 0 });
+
+        const result = await service.disable2FA(testUid, 'correct_code');
+
+        expect(result).to.be.false;
+        expect((notifyListener.notifyOnUserChange as sinon.SinonStub).called).to.be.false;
+    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/authorization/service.spec.ts` around lines 62 -
81, Add a test that covers the path where TOTP verification succeeds but the DB
update returns modifiedCount: 0: stub Users.findOneById to return a user with
services.totp enabled, stub TOTP.verify to resolve true, stub
Users.disable2FAByUserId to resolve { modifiedCount: 0 }, call
service.disable2FA with the test UID and code, assert the method returns false
and assert notifyListener.notifyOnUserChange was not called; use the same
helpers/stubs as the existing test (Users.findOneById, TOTP.verify,
Users.disable2FAByUserId, notifyListener.notifyOnUserChange) and mirror the
structure of the current success test.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18ba7a5 and f136461.

📒 Files selected for processing (3)
  • apps/meteor/app/2fa/server/methods/disable.ts
  • apps/meteor/server/services/authorization/service.spec.ts
  • apps/meteor/server/services/authorization/service.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{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/server/services/authorization/service.spec.ts
  • apps/meteor/app/2fa/server/methods/disable.ts
  • apps/meteor/server/services/authorization/service.ts
**/*.spec.ts

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

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • apps/meteor/server/services/authorization/service.spec.ts
🧠 Learnings (11)
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/server/services/authorization/service.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Group related tests in the same file

Applied to files:

  • apps/meteor/server/services/authorization/service.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • apps/meteor/server/services/authorization/service.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/server/services/authorization/service.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Maintain test isolation between test cases in Playwright tests

Applied to files:

  • apps/meteor/server/services/authorization/service.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.step()` for complex test scenarios to improve organization in Playwright tests

Applied to files:

  • apps/meteor/server/services/authorization/service.spec.ts
📚 Learning: 2025-10-06T20:30:45.540Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/storage/storage.ts:101-122
Timestamp: 2025-10-06T20:30:45.540Z
Learning: In `packages/apps-engine/tests/test-data/storage/storage.ts`, the stub methods (updatePartialAndReturnDocument, updateStatus, updateSetting, updateAppInfo, updateMarketplaceInfo) intentionally throw "Method not implemented." Tests using these methods must stub them using `SpyOn` from the test library rather than relying on actual implementations.

Applied to files:

  • apps/meteor/server/services/authorization/service.spec.ts
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/app/2fa/server/methods/disable.ts
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/app/2fa/server/methods/disable.ts
  • apps/meteor/server/services/authorization/service.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/app/2fa/server/methods/disable.ts
  • apps/meteor/server/services/authorization/service.ts
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/server/services/authorization/service.ts
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/meteor/server/services/authorization/service.ts`:
- Around line 235-239: The diff passed to notifyOnUserChange currently only
reports 'services.totp.enabled': false but disable2FAByUserId replaces the
entire services.totp object (removing secret and hashedBackup), so update the
diff in the notifyOnUserChange call to reflect the full removal (include
'services.totp.secret': null and 'services.totp.hashedBackup': null along with
'services.totp.enabled': false) or change the DB operation to use $unset and
send corresponding unset semantics; modify the notifyOnUserChange invocation in
the disable2FAByUserId flow to emit the complete field removals so clients won't
retain stale cached values.

---

Duplicate comments:
In `@apps/meteor/app/2fa/server/methods/disable.ts`:
- Line 10: The Meteor.methods call in disable.ts was not updated to use the
ServerMethods generic; update the invocation of Meteor.methods to
Meteor.methods<ServerMethods>({...}) so the runtime binding is type-checked
against the declared ServerMethods interface, matching the pattern used in
enable.ts, validateTempToken.ts, and regenerateCodes.ts and ensuring the
exported disable 2FA method signatures conform to the ServerMethods interface.

---

Nitpick comments:
In `@apps/meteor/server/services/authorization/service.spec.ts`:
- Around line 62-81: Add a test that covers the path where TOTP verification
succeeds but the DB update returns modifiedCount: 0: stub Users.findOneById to
return a user with services.totp enabled, stub TOTP.verify to resolve true, stub
Users.disable2FAByUserId to resolve { modifiedCount: 0 }, call
service.disable2FA with the test UID and code, assert the method returns false
and assert notifyListener.notifyOnUserChange was not called; use the same
helpers/stubs as the existing test (Users.findOneById, TOTP.verify,
Users.disable2FAByUserId, notifyListener.notifyOnUserChange) and mirror the
structure of the current success test.

Copy link
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.

🧹 Nitpick comments (1)
apps/meteor/server/services/authorization/service.ts (1)

1-1: Consider removing the Meteor import from this service layer.

import { Meteor } was added here solely to use Meteor.Error (lines 214, 229). This is the only method in the service that reaches directly into Meteor's error type, coupling the IAuthorization service implementation to the Meteor runtime. The interface signature (Promise<boolean>) doesn't advertise these throws, which also means consuming code in non-Meteor contexts gets an undocumented dependency.

💡 Suggested alternative
-import { Meteor } from 'meteor/meteor';
-		if (!user) {
-			throw new Meteor.Error('error-invalid-user', 'Invalid user');
-		}
+		if (!user) {
+			throw Object.assign(new Error('Invalid user'), { error: 'error-invalid-user' });
+		}
-		if (!verified) {
-			throw new Meteor.Error('invalid-totp');
-		}
+		if (!verified) {
+			throw Object.assign(new Error('Invalid TOTP token'), { error: 'invalid-totp' });
+		}

The Meteor method layer in apps/meteor/app/2fa/server/methods/disable.ts can catch and re-wrap as Meteor.Error if needed, keeping the service framework-agnostic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/server/services/authorization/service.ts` at line 1, Remove the
Meteor runtime dependency from the IAuthorization service by deleting the
`import { Meteor }` and stop throwing `Meteor.Error` inside this service;
instead throw a plain Error or a domain-specific error (e.g.,
AuthorizationError) from the service methods that currently use `Meteor.Error`
(the places in this file that construct `new Meteor.Error(...)`). Keep the
service interface and Promise<boolean> return types unchanged, and update
callers in the Meteor method layer (for example the 2FA disable method) to catch
the service Error and re-wrap it as `Meteor.Error` for transport to the client.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f136461 and d31a854.

📒 Files selected for processing (2)
  • apps/meteor/server/services/authorization/service.spec.ts
  • apps/meteor/server/services/authorization/service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/meteor/server/services/authorization/service.spec.ts
🧰 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/server/services/authorization/service.ts
🧠 Learnings (4)
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/server/services/authorization/service.ts
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/server/services/authorization/service.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/server/services/authorization/service.ts
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/server/services/authorization/service.ts
🧬 Code graph analysis (1)
apps/meteor/server/services/authorization/service.ts (2)
apps/meteor/app/2fa/server/methods/disable.ts (1)
  • code (11-19)
apps/meteor/app/lib/server/lib/notifyListener.ts (1)
  • notifyOnUserChange (377-389)
⏰ 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). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (1)
apps/meteor/server/services/authorization/service.ts (1)

221-226: No type-safety issue here. The secret and hashedBackup fields are defined as required (non-optional) properties within the totp object in the IUser type definition (packages/core-typings/src/IUser.ts lines 88–93). After the guard if (!user.services?.totp?.enabled) ensures totp exists and enabled is truthy, both secret: string and hashedBackup: string[] are guaranteed to be accessible. The call to TOTP.verify correctly passes secret (required) and backupTokens (optional), which aligns with the function signature.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/meteor/server/services/authorization/service.ts`:
- Around line 239-242: The object passed into notifyOnUserChange currently sets
'services.totp.secret' and 'services.totp.hashedBackup' to undefined which
JSON.stringify will drop, so replace those undefined entries by adding those key
paths to the notifyOnUserChange unset parameter (while keeping
'services.totp.enabled': false in the fields/modified object); update the
notifyOnUserChange call in service.ts (look for notifyOnUserChange invocation
around the block containing 'services.totp.enabled') to pass unset:
['services.totp.secret','services.totp.hashedBackup'] instead of assigning
undefined values.

---

Nitpick comments:
In `@apps/meteor/server/services/authorization/service.ts`:
- Line 1: Remove the Meteor runtime dependency from the IAuthorization service
by deleting the `import { Meteor }` and stop throwing `Meteor.Error` inside this
service; instead throw a plain Error or a domain-specific error (e.g.,
AuthorizationError) from the service methods that currently use `Meteor.Error`
(the places in this file that construct `new Meteor.Error(...)`). Keep the
service interface and Promise<boolean> return types unchanged, and update
callers in the Meteor method layer (for example the 2FA disable method) to catch
the service Error and re-wrap it as `Meteor.Error` for transport to the client.

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.

3 participants