Skip to content

Conversation

@sampaiodiego
Copy link
Member

@sampaiodiego sampaiodiego commented Jul 30, 2025

https://rocketchat.atlassian.net/browse/ARCH-1749
https://rocketchat.atlassian.net/browse/ARCH-1800

Proposed changes (including videos or screenshots)

Issue(s)

Steps to test or reproduce

Further comments

Summary by CodeRabbit

  • Bug Fixes

    • Prevents errors when enforcing login-token limits for users with no existing tokens.
  • Chores

    • Security hardening: creating user tokens now requires a server-validated secret.
    • Public API update: the previous public REST signature for creating user tokens was removed/updated—integrations must be adjusted.
  • Misc

    • Minor package and manifest housekeeping.

✏️ Tip: You can customize this high-level summary in your review settings.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jul 30, 2025

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

  • This PR is targeting the wrong base branch. It should target 7.14.0, but it targets 8.0.0

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 Jul 30, 2025

🦋 Changeset detected

Latest commit: 03ec248

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 39 packages
Name Type
@rocket.chat/rest-typings Minor
@rocket.chat/meteor Minor
@rocket.chat/api-client Patch
@rocket.chat/core-services Patch
@rocket.chat/ddp-client Patch
@rocket.chat/http-router Patch
@rocket.chat/models Patch
@rocket.chat/ui-contexts Major
@rocket.chat/web-ui-registration Major
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/federation-matrix Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/presence Patch
rocketchat-services Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/network-broker Patch
@rocket.chat/omni-core-ee Patch
@rocket.chat/livechat Patch
@rocket.chat/mock-providers Patch
@rocket.chat/cron Patch
@rocket.chat/instance-status Patch
@rocket.chat/omni-core Patch
@rocket.chat/ui-client Major
@rocket.chat/media-calls Patch
@rocket.chat/uikit-playground Patch
@rocket.chat/fuselage-ui-kit Major
@rocket.chat/gazzodown Major
@rocket.chat/ui-avatar Major
@rocket.chat/ui-video-conf Major
@rocket.chat/ui-voip Major
@rocket.chat/core-typings Minor
@rocket.chat/apps Patch
@rocket.chat/model-typings Patch
@rocket.chat/license Patch
@rocket.chat/pdf-worker Patch

Not sure what this means? Click here to learn what changesets are.

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

@github-actions
Copy link
Contributor

PR Preview Action v1.6.2

🚀 View preview at
https://RocketChat.github.io/Rocket.Chat/pr-preview/pr-36570/

Built to branch gh-pages at 2025-07-30 13:34 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@codecov
Copy link

codecov bot commented Jul 30, 2025

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 66.92%. Comparing base (d77cd2f) to head (c91a367).
⚠️ Report is 3 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #36570      +/-   ##
===========================================
- Coverage    67.74%   66.92%   -0.82%     
===========================================
  Files         3457     3471      +14     
  Lines       113708   115054    +1346     
  Branches     20903    21311     +408     
===========================================
- Hits         77026    77005      -21     
- Misses       34547    35987    +1440     
+ Partials      2135     2062      -73     
Flag Coverage Δ
e2e 57.36% <ø> (-0.02%) ⬇️
e2e-api 42.25% <0.00%> (-1.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.

rodrigok
rodrigok previously approved these changes Sep 25, 2025
@sampaiodiego sampaiodiego marked this pull request as ready for review September 26, 2025 21:53
@sampaiodiego sampaiodiego requested review from a team as code owners September 26, 2025 21:53
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 26, 2025

Walkthrough

Replaces the previous users.createToken flow with a secret-gated implementation: the REST endpoint and server DDP method now require a provided secret validated against CREATE_TOKENS_FOR_USERS_SECRET; generateAccessToken signature changed to accept (userId, secret); login-token handling hardened and REST typings updated/removed. (41 words)

Changes

Cohort / File(s) Summary
Changeset note
\.changeset/afraid-parents-bake.md
Adds a changeset documenting that /v1/users.createToken now requires a secret validated against CREATE_TOKENS_FOR_USERS_SECRET.
REST route + typings augmentation
apps/meteor/app/api/server/v1/users.ts
Adds a strongly-typed users.createToken route requiring { userId, secret } with AJV validation; returns { data: { userId, authToken }, success }; introduces UsersEndpoints = ExtractRoutesFromAPI<typeof usersEndpoints> and augments @rocket.chat/rest-typings.
Server DDP method & token generation
apps/meteor/app/lib/server/methods/createToken.ts
Adds createToken(userId) server method; changes generateAccessToken signature to (userId, secret); replaces permission/flag checks with strict secret comparison against CREATE_TOKENS_FOR_USERS_SECRET, throwing MeteorError on mismatch; token creation and persistence flow retained.
Login tokens limit robustness
apps/meteor/server/services/user/service.ts
Adds default destructuring for resume tokens: const [{ tokens } = { tokens: [] }] = ... so token-limit enforcement handles missing/empty results safely.
REST typings removal
packages/rest-typings/src/v1/users.ts
Removes the public POST /v1/users.createToken endpoint declaration from the REST typings.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant REST as "REST /v1/users.createToken"
  participant Env as "Env (CREATE_TOKENS_FOR_USERS_SECRET)"
  participant Service as "Token Service / Method"
  participant DB as "Users DB"

  Client->>REST: POST { userId, secret }
  REST->>Env: read CREATE_TOKENS_FOR_USERS_SECRET
  alt secret matches
    REST->>Service: generateAccessToken(userId, secret)
    Service->>DB: insert login token
    Service->>DB: ensureLoginTokensLimit(uid) (tokens default to [])
    Service-->>REST: { userId, authToken }
    REST-->>Client: 200 { data: { userId, authToken }, success: true }
  else secret mismatch
    REST-->>Client: 400 / MeteorError (not authorized)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing attention:
    • Call sites and imports impacted by generateAccessToken(userId, secret) signature change.
    • Consistency between removed packages/rest-typings endpoint and new route/type augmentation.
    • Behavior and handling when CREATE_TOKENS_FOR_USERS_SECRET is undefined (errors/tests).
    • DDP method exposure and client expectations for createToken.

Suggested labels

stat: ready to merge

Suggested reviewers

  • aleksandernsilva

Poem

A rabbit taps a secret key,
Quiet hops, a token tree.
Routes now ask the gate to see,
Tokens kept where they should be.
Hop safe, hop snug — secure and free! 🐇🔐

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: undeprecating the /v1/users.createToken endpoint with enhanced security requirements via a secret parameter.
Linked Issues check ✅ Passed The PR successfully implements both linked issue objectives: removes deprecation from /v1/users.createToken (ARCH-1800) and updates the createToken method with new security configuration (ARCH-1749).
Out of Scope Changes check ✅ Passed All code changes directly relate to undeprecating and securing the /v1/users.createToken endpoint; the addition of a default value in token limit handling is a supporting fix.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch undeprecate-create-tokens-endpoint

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4224d8a and 03ec248.

📒 Files selected for processing (3)
  • .changeset/afraid-parents-bake.md (1 hunks)
  • apps/meteor/app/api/server/v1/users.ts (4 hunks)
  • apps/meteor/app/lib/server/methods/createToken.ts (1 hunks)
🧰 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/app/api/server/v1/users.ts
  • apps/meteor/app/lib/server/methods/createToken.ts
🧠 Learnings (6)
📚 Learning: 2025-09-15T06:21:00.139Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 36868
File: apps/meteor/app/apps/server/bridges/serverEndpoints.ts:35-48
Timestamp: 2025-09-15T06:21:00.139Z
Learning: In ServerEndpointsBridge.ts, the permission model distinguishes between token pass-through and true impersonation: `server-endpoints.call` is required for all endpoint access, while `server-endpoints.impersonate` is only required when `info.user.id` is provided without `info.user.token` (lines 48-53), meaning the bridge needs to mint a token. When both user ID and token are provided, it's considered legitimate credential usage, not impersonation.

Applied to files:

  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/lib/server/methods/createToken.ts
📚 Learning: 2025-09-15T06:21:00.139Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 36868
File: apps/meteor/app/apps/server/bridges/serverEndpoints.ts:35-48
Timestamp: 2025-09-15T06:21:00.139Z
Learning: In ServerEndpointsBridge.ts, the impersonation permission (`server-endpoints.impersonate`) is enforced at lines 48-49 specifically when `info.user` is provided without `info.user.token`, which represents the impersonation scenario where a token needs to be minted. Token pass-through (when both user ID and token are provided) doesn't require the impersonate permission since the app already possesses a valid token.

Applied to files:

  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/lib/server/methods/createToken.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/api/server/v1/users.ts
  • apps/meteor/app/lib/server/methods/createToken.ts
📚 Learning: 2025-10-07T15:08:37.419Z
Learnt from: cardoso
Repo: RocketChat/Rocket.Chat PR: 36942
File: apps/meteor/client/lib/e2ee/pbkdf2.ts:13-45
Timestamp: 2025-10-07T15:08:37.419Z
Learning: In apps/meteor/client/lib/e2ee/pbkdf2.ts, PBKDF2 iteration count validation is not enforced because the iterations parameter is outside the user's control and is system-managed.

Applied to files:

  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/app/lib/server/methods/createToken.ts
📚 Learning: 2025-11-19T12:32:29.696Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37547
File: packages/i18n/src/locales/en.i18n.json:634-634
Timestamp: 2025-11-19T12:32:29.696Z
Learning: Repo: RocketChat/Rocket.Chat
Context: i18n workflow
Learning: In this repository, new translation keys should be added to packages/i18n/src/locales/en.i18n.json only; other locale files are populated via the external translation pipeline and/or fall back to English. Do not request adding the same key to all locale files in future reviews.

Applied to files:

  • .changeset/afraid-parents-bake.md
📚 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/app/lib/server/methods/createToken.ts
🧬 Code graph analysis (2)
apps/meteor/app/api/server/v1/users.ts (2)
apps/meteor/app/lib/server/methods/createToken.ts (1)
  • generateAccessToken (13-27)
apps/meteor/app/api/server/ApiClass.ts (1)
  • ExtractRoutesFromAPI (73-77)
apps/meteor/app/lib/server/methods/createToken.ts (2)
packages/ddp-client/src/types/methods.ts (1)
  • ServerMethods (2-6)
packages/core-services/src/index.ts (1)
  • MeteorError (52-52)
⏰ 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). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (3)
.changeset/afraid-parents-bake.md (1)

1-6: LGTM!

The changeset accurately documents the breaking change: the endpoint is no longer deprecated, and it now requires a secret parameter validated against the CREATE_TOKENS_FOR_USERS_SECRET environment variable.

apps/meteor/app/api/server/v1/users.ts (1)

1488-1493: LGTM!

The type augmentation correctly extends the Endpoints interface with the new endpoint definition, ensuring type-safe API usage across the codebase.

apps/meteor/app/lib/server/methods/createToken.ts (1)

4-9: The DDP method signature is correct and requires no changes.

The createToken DDP method signature (lines 4-9) declaring only userId is appropriate for the DDP interface. The exported generateAccessToken function is a separate utility used by the REST endpoint (users.createToken), which accepts the secret as a request body parameter. These represent different code paths: the DDP method will need its own implementation that can validate authorization, while the REST endpoint handler already passes the client-provided secret to generateAccessToken for validation. No mismatch exists between the DDP declaration and the function signature.

Likely an incorrect or invalid review comment.


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.

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/meteor/app/lib/server/methods/createToken.ts (1)

11-11: Fix the secret argument flow in createToken.

generateAccessToken now requires (userId, secret), but createToken still calls it with the old order. As a result, the second argument is the requested user ID (from clients still calling with a user ID), so the secret comparison fails and the method always throws error-not-authorized. Please update the method signature (and its declaration) to accept the secret explicitly and forward it correctly.

-		createToken(userId: string): { userId: string; authToken: string };
+		createToken(secret: string): { userId: string; authToken: string };
@@
-Meteor.methods<ServerMethods>({
-	async createToken(userId) {
+Meteor.methods<ServerMethods>({
+	async createToken(secret) {
 		methodDeprecationLogger.method('createToken', '8.0.0', '/v1/users.createToken');
 
 		const callee = Meteor.userId();
 		if (!callee) {
 			throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'createToken' });
 		}
 
-		return generateAccessToken(callee, userId);
+		return generateAccessToken(callee, secret);
 	},
 });

Also applies to: 34-43

🧹 Nitpick comments (1)
apps/meteor/app/api/server/v1/users.ts (1)

781-799: Require authToken in the success schema.

Line 789 only marks userId as required. Because this schema feeds ExtractRoutesFromAPI, the generated typings (and the runtime validator) will treat authToken as optional even though the route always returns it. That weakens the contract and would let a regression slip through unnoticed. Please add authToken to the required array.

-						required: ['userId'],
+						required: ['userId', 'authToken'],
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 23ba66a and ae7d1f9.

📒 Files selected for processing (5)
  • .changeset/afraid-parents-bake.md (1 hunks)
  • apps/meteor/app/api/server/v1/users.ts (4 hunks)
  • apps/meteor/app/lib/server/methods/createToken.ts (2 hunks)
  • apps/meteor/server/services/user/service.ts (1 hunks)
  • packages/rest-typings/src/v1/users.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/rest-typings/src/v1/users.ts
🧰 Additional context used
🧬 Code graph analysis (2)
apps/meteor/app/api/server/v1/users.ts (5)
packages/rest-typings/src/v1/Ajv.ts (1)
  • ajv (23-23)
apps/meteor/app/lib/server/methods/createToken.ts (1)
  • generateAccessToken (17-31)
packages/rest-typings/src/v1/users.ts (1)
  • UsersEndpoints (131-389)
apps/meteor/app/api/server/ApiClass.ts (1)
  • ExtractRoutesFromAPI (73-77)
packages/rest-typings/src/index.ts (1)
  • Endpoints (51-98)
apps/meteor/app/lib/server/methods/createToken.ts (1)
packages/core-services/src/index.ts (1)
  • MeteorError (56-56)
⏰ 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). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build

ggazzo
ggazzo previously approved these changes Sep 30, 2025
@ggazzo ggazzo added the stat: QA assured Means it has been tested and approved by a company insider label Sep 30, 2025
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Sep 30, 2025
@dionisio-bot dionisio-bot bot removed the stat: ready to merge PR tested and approved waiting for merge label Dec 16, 2025
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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ae7d1f9 and c91a367.

📒 Files selected for processing (1)
  • apps/meteor/app/api/server/v1/users.ts (4 hunks)
🧰 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/app/api/server/v1/users.ts
🧠 Learnings (2)
📚 Learning: 2025-09-15T06:21:00.139Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 36868
File: apps/meteor/app/apps/server/bridges/serverEndpoints.ts:35-48
Timestamp: 2025-09-15T06:21:00.139Z
Learning: In ServerEndpointsBridge.ts, the permission model distinguishes between token pass-through and true impersonation: `server-endpoints.call` is required for all endpoint access, while `server-endpoints.impersonate` is only required when `info.user.id` is provided without `info.user.token` (lines 48-53), meaning the bridge needs to mint a token. When both user ID and token are provided, it's considered legitimate credential usage, not impersonation.

Applied to files:

  • apps/meteor/app/api/server/v1/users.ts
📚 Learning: 2025-09-15T06:21:00.139Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 36868
File: apps/meteor/app/apps/server/bridges/serverEndpoints.ts:35-48
Timestamp: 2025-09-15T06:21:00.139Z
Learning: In ServerEndpointsBridge.ts, the impersonation permission (`server-endpoints.impersonate`) is enforced at lines 48-49 specifically when `info.user` is provided without `info.user.token`, which represents the impersonation scenario where a token needs to be minted. Token pass-through (when both user ID and token are provided) doesn't require the impersonate permission since the app already possesses a valid token.

Applied to files:

  • apps/meteor/app/api/server/v1/users.ts
🧬 Code graph analysis (1)
apps/meteor/app/api/server/v1/users.ts (4)
apps/meteor/app/api/server/index.ts (1)
  • API (55-55)
packages/rest-typings/src/v1/Ajv.ts (1)
  • ajv (24-24)
apps/meteor/app/lib/server/methods/createToken.ts (1)
  • generateAccessToken (17-31)
apps/meteor/app/api/server/ApiClass.ts (1)
  • ExtractRoutesFromAPI (73-77)
⏰ 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). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (3)
apps/meteor/app/api/server/v1/users.ts (3)

21-21: LGTM: Imports support the new validated endpoint.

The ajv import enables runtime validation for the endpoint, and ExtractRoutesFromAPI allows proper type extraction for REST typings augmentation.

Also applies to: 73-73


819-826: LGTM: Token generation logic is correct.

The implementation properly:

  • Validates the user via getUserFromParams
  • Delegates secret validation and token generation to generateAccessToken
  • Returns the structured response

Error handling for invalid secrets is properly delegated to the generateAccessToken function, which throws a MeteorError when the secret doesn't match.


1488-1493: LGTM: Type exports and module augmentation are correctly implemented.

The pattern properly:

  • Extracts route types from the endpoint definition using ExtractRoutesFromAPI
  • Augments the REST typings module to expose the endpoint signatures
  • Maintains type safety for API consumers

Comment on lines +761 to +819
const usersEndpoints = API.v1.post(
'users.createToken',
{ authRequired: true, deprecationVersion: '8.0.0' },
{
async post() {
const user = await getUserFromParams(this.bodyParams);
authRequired: true,
body: ajv.compile<{ userId: string; secret: string }>({
type: 'object',
properties: {
userId: {
type: 'string',
minLength: 1,
},
secret: {
type: 'string',
minLength: 1,
},
},
required: ['userId', 'secret'],
additionalProperties: false,
}),
response: {
200: ajv.compile<{ data: { userId: string; authToken: string } }>({
type: 'object',
properties: {
data: {
type: 'object',
properties: {
userId: {
type: 'string',
minLength: 1,
},
authToken: {
type: 'string',
minLength: 1,
},
},
required: ['userId'],
additionalProperties: false,
},
success: {
type: 'boolean',
enum: [true],
},
},
required: ['data', 'success'],
additionalProperties: false,
}),
400: ajv.compile({
type: 'object',
properties: {
success: { type: 'boolean', enum: [false] },
error: { type: 'string' },
errorType: { type: 'string' },
},
required: ['success'],
additionalProperties: false,
}),
},
},
async function action() {
Copy link
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
# Read the users.ts file around the endpoint definition
head -n 850 apps/meteor/app/api/server/v1/users.ts | tail -n 100

Repository: RocketChat/Rocket.Chat

Length of output: 2135


🏁 Script executed:

#!/bin/bash
# First, check file size and line count
wc -l apps/meteor/app/api/server/v1/users.ts

# Read the specific section with the endpoint definition
sed -n '761,819p' apps/meteor/app/api/server/v1/users.ts

Repository: RocketChat/Rocket.Chat

Length of output: 1331


🏁 Script executed:

#!/bin/bash
# Search for hasPermission checks in the file
rg -n -A 5 "hasPermission.*create.*token" apps/meteor/app/api/server/v1/users.ts

# Also search for the createToken endpoint handler
rg -n -A 30 "users\.createToken" apps/meteor/app/api/server/v1/users.ts | head -60

# Check for rate limiting patterns
rg -n "rateLimiterOptions" apps/meteor/app/api/server/v1/users.ts

# Check for audit logging
rg -n "Audit\." apps/meteor/app/api/server/v1/users.ts | head -20

Repository: RocketChat/Rocket.Chat

Length of output: 854


🏁 Script executed:

#!/bin/bash
# Get more lines to see the complete endpoint
sed -n '810,850p' apps/meteor/app/api/server/v1/users.ts

# Find the generateAccessToken function
rg -n -B 5 -A 20 "generateAccessToken" apps/meteor/app/api/server/v1/users.ts | head -50

# Check if this endpoint has rateLimiterOptions by reading larger range
sed -n '760,825p' apps/meteor/app/api/server/v1/users.ts

# Search for all endpoints near createToken to understand pattern
rg -n "API\.v1\.(post|get|put|delete)" apps/meteor/app/api/server/v1/users.ts | grep -A 2 -B 2 "createToken"

Repository: RocketChat/Rocket.Chat

Length of output: 4551


🏁 Script executed:

#!/bin/bash
# Let's look at the imports to understand what getUserFromParams and generateAccessToken do
head -100 apps/meteor/app/api/server/v1/users.ts

# Also check the actual location of generateAccessToken function
rg -n "^(export )?(async )?function generateAccessToken|^const generateAccessToken" apps/meteor/app/api/server/v1/users.ts

Repository: RocketChat/Rocket.Chat

Length of output: 5662


🏁 Script executed:

#!/bin/bash
# Search for imports and helper functions
rg -n "generateAccessToken|getUserFromParams" apps/meteor/app/api/server/v1/users.ts

# Also find where the endpoint definition closes and look for rateLimiterOptions
sed -n '760,830p' apps/meteor/app/api/server/v1/users.ts | cat -n

Repository: RocketChat/Rocket.Chat

Length of output: 2869


🏁 Script executed:

#!/bin/bash
# Find and read the generateAccessToken function
find apps/meteor -name "createToken.ts" -o -name "createToken.js" | head -5

# Also search more broadly
rg -l "generateAccessToken" apps/meteor --type=ts | head -5

Repository: RocketChat/Rocket.Chat

Length of output: 204


🏁 Script executed:

#!/bin/bash
# Read the createToken module
cat apps/meteor/app/lib/server/methods/createToken.ts

# Also search for hasPermission checks related to token creation
rg -n "hasPermission.*token" apps/meteor --type=ts -i | head -20

Repository: RocketChat/Rocket.Chat

Length of output: 1981


🏁 Script executed:

#!/bin/bash
# Let's check if there's a getUserFromParams implementation that might have permission checks
cat apps/meteor/app/api/helpers/getUserFromParams.ts

# And check for rateLimiterOptions in the entire API file for pattern understanding
rg -B 5 -A 5 "rateLimiterOptions" apps/meteor/app/api/server/v1/users.ts | head -40

Repository: RocketChat/Rocket.Chat

Length of output: 470


🏁 Script executed:

#!/bin/bash
# Check if createToken endpoint has rateLimiterOptions
sed -n '761,827p' apps/meteor/app/api/server/v1/users.ts | grep -A 2 -B 2 "rateLimiterOptions\|numRequestsAllowed"

# Search for any rateLimiterOptions in context of createToken
rg -B 20 "users\.createToken" apps/meteor/app/api/server/v1/users.ts | grep -A 15 "rateLimiterOptions\|numRequestsAllowed"

# Check all endpoints to see if createToken has rate limiting in its options object
sed -n '761,830p' apps/meteor/app/api/server/v1/users.ts

Repository: RocketChat/Rocket.Chat

Length of output: 1532


🏁 Script executed:

#!/bin/bash
# Search for audit logging patterns related to token creation
rg -n "Audit\.|audit" apps/meteor/app/lib/server/methods/createToken.ts

# Check for any logging in generateAccessToken
rg -n "logger\|log\|audit" apps/meteor/app/lib/server/methods/createToken.ts

# Look for UserChangedAuditStore which was imported
rg -n "UserChangedAuditStore" apps/meteor/app/api/server/v1/users.ts | head -10

# Check getUserFromParams implementation
find apps/meteor -path "*/api/helpers/*" -name "*.ts" | xargs ls -la

Repository: RocketChat/Rocket.Chat

Length of output: 315


🏁 Script executed:

#!/bin/bash
# Find getUserFromParams helper
find apps/meteor -name "getUserFromParams*" -o -name "*getUserFromParams*" | grep -E "\.(ts|js)$"

# Search for it in the api directory
rg -l "export.*getUserFromParams" apps/meteor/app/api --type=ts

Repository: RocketChat/Rocket.Chat

Length of output: 177


🏁 Script executed:

#!/bin/bash
# Check the full implementation details
rg -B 5 -A 30 "export.*function getUserFromParams|export const getUserFromParams" apps/meteor/app/api --type=ts

# Also check what other sensitive endpoints do for comparison
rg -B 10 "users\.generatePersonalAccessToken\|users\.setUserStatus\|users\.create" apps/meteor/app/api/server/v1/users.ts | grep -A 10 -B 10 "hasPermission\|rateLimiter"

Repository: RocketChat/Rocket.Chat

Length of output: 3370


🏁 Script executed:

#!/bin/bash
# Look at how generatePersonalAccessToken endpoint is defined for comparison
rg -B 5 -A 30 "users\.generatePersonalAccessToken" apps/meteor/app/api/server/v1/users.ts | head -60

# Also look at how users.create handles permissions
rg -B 5 -A 30 "users\.create" apps/meteor/app/api/server/v1/users.ts | head -60

Repository: RocketChat/Rocket.Chat

Length of output: 2507


🏁 Script executed:

#!/bin/bash
# Look specifically at the endpoint options for createToken
sed -n '761,768p' apps/meteor/app/api/server/v1/users.ts

# Check if there's a validateParams or permission check option we might have missed
sed -n '761,820p' apps/meteor/app/api/server/v1/users.ts | grep -i "permission\|validate\|rateLimiter"

# Compare structure with a known secure endpoint
rg -B 2 -A 15 "authRequired: true," apps/meteor/app/api/server/v1/users.ts | head -80

Repository: RocketChat/Rocket.Chat

Length of output: 2854


🏁 Script executed:

#!/bin/bash
# Check the deprecation status and any related PRs or changes
rg -n "CREATE_TOKENS_FOR_USERS\|deprecated\|8.0.0" apps/meteor/app/lib/server/methods/createToken.ts

# Search for any permission checks that might be in the endpoint definition itself
sed -n '761,827p' apps/meteor/app/api/server/v1/users.ts | cat -n

# Check if there are any authorization middleware or decorators
rg -B 10 "async function action" apps/meteor/app/api/server/v1/users.ts | grep -A 10 "createToken"

Repository: RocketChat/Rocket.Chat

Length of output: 1934


🏁 Script executed:

#!/bin/bash
# Check PR context or related changes
git log --oneline --all --grep="createToken\|users.createToken" 2>/dev/null | head -20

# Look for any recent changes to the endpoint
git log --oneline -p apps/meteor/app/api/server/v1/users.ts 2>/dev/null | grep -A 20 "users.createToken" | head -40

# Check if there are permission checks in any middleware
rg -n "hasPermission" apps/meteor/app/api/server/v1/users.ts | grep -i "token\|create"

Repository: RocketChat/Rocket.Chat

Length of output: 718


Endpoint lacks permission checks, rate limiting, and audit logging.

The endpoint documentation indicates it should require the user-generate-access-token permission, but the implementation contains only authRequired: true with no explicit permission check. The sole protection mechanism is the CREATE_TOKENS_FOR_USERS_SECRET environment variable validation in generateAccessToken(), meaning any authenticated user with access to the secret can create tokens for any other user.

The endpoint lacks:

  • Permission checks: No hasPermissionAsync() call for user-generate-access-token or equivalent
  • Rate limiting: No rateLimiterOptions defined (unlike authentication endpoints such as users.register)
  • Audit logging: No UserChangedAuditStore or token creation audit records

Documented security issue: users with token creation capability can create tokens for administrators and use them for unauthorized actions. Consider implementing role-based permission checks, rate limiting, and audit logging for this sensitive endpoint before the 8.0.0 deprecation removal.

minLength: 1,
},
},
required: ['userId'],
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the response schema: authToken should be required.

The required array only includes userId, but generateAccessToken always returns both userId and authToken (as seen in the relevant code snippet). The response schema should reflect this.

Apply this diff:

-						required: ['userId'],
+						required: ['userId', 'authToken'],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
required: ['userId'],
required: ['userId', 'authToken'],
🤖 Prompt for AI Agents
In apps/meteor/app/api/server/v1/users.ts around line 796, the JSON response
schema's required array only lists 'userId' but the function always returns both
'userId' and 'authToken'; update the schema so the required array includes both
'userId' and 'authToken' (e.g. required: ['userId','authToken']) to accurately
reflect the response shape.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 16, 2025

📦 Docker Image Size Report

📈 Changes

Service Current Baseline Change Percent
sum of all images 1.1GiB 1.1GiB +8.1MiB
rocketchat 355MiB 347MiB +8.4MiB
omnichannel-transcript-service 132MiB 132MiB -89KiB
queue-worker-service 132MiB 132MiB -91KiB
ddp-streamer-service 126MiB 126MiB -41KiB
account-service 113MiB 113MiB -40KiB
presence-service 110MiB 110MiB -39KiB
authorization-service 110MiB 110MiB -38KiB

📊 Historical Trend

---
config:
  theme: "dark"
  xyChart:
    width: 900
    height: 400
---
xychart
  title "Image Size Evolution by Service (Last 30 Days + This PR)"
  x-axis ["11/15 22:28", "11/16 01:28", "11/17 23:50", "11/18 22:53", "11/19 23:02", "11/21 16:49", "11/24 17:34", "11/27 22:32", "11/28 19:05", "12/01 23:01", "12/02 21:57", "12/03 21:00", "12/04 18:17", "12/05 21:56", "12/08 20:15", "12/09 22:17", "12/10 23:26", "12/11 21:56", "12/12 22:45", "12/13 01:34", "12/15 22:31", "12/16 22:18", "12/17 17:50", "12/17 19:27 (PR)"]
  y-axis "Size (GB)" 0 --> 0.5
  line "account-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
  line "authorization-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
  line "ddp-streamer-service" [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12]
  line "omnichannel-transcript-service" [0.14, 0.14, 0.14, 0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13]
  line "presence-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11]
  line "queue-worker-service" [0.14, 0.14, 0.14, 0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13]
  line "rocketchat" [0.36, 0.36, 0.35, 0.35, 0.35, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.35]
Loading

Statistics (last 23 days):

  • 📊 Average: 1.5GiB
  • ⬇️ Minimum: 1.2GiB
  • ⬆️ Maximum: 1.6GiB
  • 🎯 Current PR: 1.1GiB
ℹ️ About this report

This report compares Docker image sizes from this build against the develop baseline.

  • Tag: pr-36570
  • Baseline: develop
  • Timestamp: 2025-12-17 19:27:52 UTC
  • Historical data points: 23

Updated: Wed, 17 Dec 2025 19:27:52 GMT

@ggazzo ggazzo modified the milestones: 7.11.0, 7.14.0 Dec 17, 2025
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Dec 17, 2025
@ggazzo ggazzo changed the base branch from develop to release-8.0.0 December 17, 2025 13:10
@ggazzo ggazzo dismissed their stale review December 17, 2025 13:10

The base branch was changed.

@ggazzo ggazzo requested review from a team as code owners December 17, 2025 13:10
@ggazzo ggazzo force-pushed the undeprecate-create-tokens-endpoint branch from c91a367 to 4224d8a Compare December 17, 2025 13:13
@dionisio-bot dionisio-bot bot removed the stat: ready to merge PR tested and approved waiting for merge label Dec 17, 2025
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/meteor/app/lib/server/methods/createToken.ts (1)

13-27: Add timing-safe secret comparison and explicit user validation.

The REST API endpoint validates user existence via getUserFromParams() before calling this function, but the function itself lacks internal validation. For defense in depth, add an explicit check. Additionally, the secret comparison should use timing-safe comparison to prevent timing attacks:

export async function generateAccessToken(userId: string, secret: string) {
	if (!secret || !CREATE_TOKENS_FOR_USERS_SECRET || !crypto.timingSafeEqual(Buffer.from(secret), Buffer.from(CREATE_TOKENS_FOR_USERS_SECRET))) {
		throw new MeteorError('error-not-authorized', 'Not authorized');
	}

+	const user = await User.findOneById(userId);
+	if (!user) {
+		throw new MeteorError('error-invalid-user', 'User not found');
+	}

	const token = Accounts._generateStampedLoginToken();
	Accounts._insertLoginToken(userId, token);

	await User.ensureLoginTokensLimit(userId);

	return {
		userId,
		authToken: token.token,
	};
}

The timing-safe comparison is critical to prevent attackers from using timing analysis to guess the secret. User validation should be explicit at the function level to prevent issues if this is called from other contexts in the future.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c91a367 and 4224d8a.

📒 Files selected for processing (3)
  • .changeset/afraid-parents-bake.md (1 hunks)
  • apps/meteor/app/api/server/v1/users.ts (4 hunks)
  • apps/meteor/app/lib/server/methods/createToken.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/meteor/app/api/server/v1/users.ts
  • .changeset/afraid-parents-bake.md
🧰 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/app/lib/server/methods/createToken.ts
🧠 Learnings (4)
📚 Learning: 2025-09-15T06:21:00.139Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 36868
File: apps/meteor/app/apps/server/bridges/serverEndpoints.ts:35-48
Timestamp: 2025-09-15T06:21:00.139Z
Learning: In ServerEndpointsBridge.ts, the permission model distinguishes between token pass-through and true impersonation: `server-endpoints.call` is required for all endpoint access, while `server-endpoints.impersonate` is only required when `info.user.id` is provided without `info.user.token` (lines 48-53), meaning the bridge needs to mint a token. When both user ID and token are provided, it's considered legitimate credential usage, not impersonation.

Applied to files:

  • apps/meteor/app/lib/server/methods/createToken.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/app/lib/server/methods/createToken.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/lib/server/methods/createToken.ts
📚 Learning: 2025-09-15T06:21:00.139Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 36868
File: apps/meteor/app/apps/server/bridges/serverEndpoints.ts:35-48
Timestamp: 2025-09-15T06:21:00.139Z
Learning: In ServerEndpointsBridge.ts, the impersonation permission (`server-endpoints.impersonate`) is enforced at lines 48-49 specifically when `info.user` is provided without `info.user.token`, which represents the impersonation scenario where a token needs to be minted. Token pass-through (when both user ID and token are provided) doesn't require the impersonate permission since the app already possesses a valid token.

Applied to files:

  • apps/meteor/app/lib/server/methods/createToken.ts
🔇 Additional comments (2)
apps/meteor/app/lib/server/methods/createToken.ts (2)

1-3: LGTM!

The imports are appropriate for the token generation functionality.


4-9: LGTM!

The TypeScript augmentation properly declares the createToken method signature for the public API. The signature difference between this declaration (taking only userId) and the generateAccessToken implementation (taking userId and secret) is expected, as the actual method registration likely supplies the secret internally.

}
}

const { CREATE_TOKENS_FOR_USERS_SECRET } = process.env;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Validate that the secret environment variable is defined and non-empty.

If CREATE_TOKENS_FOR_USERS_SECRET is not set in the environment, it will be undefined. This creates a critical security vulnerability where the validation on line 14 could be bypassed if an attacker passes undefined as the secret parameter (since undefined !== undefined is false).

Apply this diff to add defensive validation:

-const { CREATE_TOKENS_FOR_USERS_SECRET } = process.env;
+const { CREATE_TOKENS_FOR_USERS_SECRET } = process.env;
+
+if (!CREATE_TOKENS_FOR_USERS_SECRET || CREATE_TOKENS_FOR_USERS_SECRET.trim() === '') {
+	throw new Error('CREATE_TOKENS_FOR_USERS_SECRET environment variable must be set and non-empty');
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { CREATE_TOKENS_FOR_USERS_SECRET } = process.env;
const { CREATE_TOKENS_FOR_USERS_SECRET } = process.env;
if (!CREATE_TOKENS_FOR_USERS_SECRET || CREATE_TOKENS_FOR_USERS_SECRET.trim() === '') {
throw new Error('CREATE_TOKENS_FOR_USERS_SECRET environment variable must be set and non-empty');
}
🤖 Prompt for AI Agents
In apps/meteor/app/lib/server/methods/createToken.ts around line 11, the code
reads the CREATE_TOKENS_FOR_USERS_SECRET env var but doesn't validate it; add a
defensive check that ensures process.env.CREATE_TOKENS_FOR_USERS_SECRET is a
defined, non-empty string (e.g. typeof value === 'string' && value.trim().length
> 0) and if not, throw an Error (or fail fast) so the server refuses to start or
the module fails to load; this prevents an attacker from passing undefined and
bypassing the secret check and ensures later comparisons are against a
guaranteed string.

Comment on lines +14 to +15
if (secret !== CREATE_TOKENS_FOR_USERS_SECRET) {
throw new MeteorError('error-not-authorized', 'Not authorized');
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Use constant-time comparison for secret validation.

The direct string comparison (!==) is vulnerable to timing attacks, which could allow an attacker to gradually guess the secret by measuring response times. Additionally, if CREATE_TOKENS_FOR_USERS_SECRET is undefined, this check can be bypassed.

Apply this diff to use a constant-time comparison:

+import crypto from 'crypto';
+
 export async function generateAccessToken(userId: string, secret: string) {
-	if (secret !== CREATE_TOKENS_FOR_USERS_SECRET) {
+	if (!secret || !CREATE_TOKENS_FOR_USERS_SECRET || !crypto.timingSafeEqual(Buffer.from(secret), Buffer.from(CREATE_TOKENS_FOR_USERS_SECRET))) {
 		throw new MeteorError('error-not-authorized', 'Not authorized');
 	}

Note: This assumes both strings are the same length. For production use, consider using a dedicated constant-time comparison library or padding the strings to the same length before comparison.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/meteor/app/lib/server/methods/createToken.ts around lines 14-15, the
direct string comparison of the provided secret to
CREATE_TOKENS_FOR_USERS_SECRET must be replaced with a constant-time comparison
and a guard against undefined to prevent timing attacks and bypasses; convert
both values to Buffers (or Uint8Array), ensure both are defined and have equal
byte length, and then use crypto.timingSafeEqual to compare them, throwing the
same MeteorError when the check fails; if lengths differ or either value is
undefined, treat as non-equal and throw the MeteorError.

@ggazzo ggazzo changed the title feat: Undeprecate /v1/users.createToken endpoint feat!: Undeprecate /v1/users.createToken endpoint Dec 17, 2025
@ggazzo ggazzo requested a review from a team as a code owner December 17, 2025 16:50
@ggazzo ggazzo force-pushed the undeprecate-create-tokens-endpoint branch from 4224d8a to 03ec248 Compare December 17, 2025 18:53
@ggazzo ggazzo merged commit 11e89e2 into release-8.0.0 Dec 17, 2025
42 of 43 checks passed
@ggazzo ggazzo deleted the undeprecate-create-tokens-endpoint branch December 17, 2025 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants