Skip to content

Fix: Replaced jsonwebtoken with jose - a modern, ESM-native JWT library#140

Draft
sacOO7 wants to merge 1 commit intomainfrom
chore/replace-jsonwebtoken-with-jose
Draft

Fix: Replaced jsonwebtoken with jose - a modern, ESM-native JWT library#140
sacOO7 wants to merge 1 commit intomainfrom
chore/replace-jsonwebtoken-with-jose

Conversation

@sacOO7
Copy link

@sacOO7 sacOO7 commented Feb 26, 2026

  • Fixed the pnpm install failure (Node.js v25.3.0) caused by TypeError: Cannot read properties of undefined (reading 'prototype').
  • Root Cause: The jsonwebtoken package depends on buffer-equal-constant-time@1.0.1 → jwa@1.4.1, which accesses SlowBuffer.prototype at module load time. SlowBuffer was removed in Node.js v22+, but the system was running Node.js v25.3.0, causing an immediate crash when jsonwebtoken was imported during oclif manifest.

Comparison Table

Feature jose jsonwebtoken
Runtime Support Node.js, Browser, Cloudflare Workers, Deno, Bun Node.js only
Dependencies Zero (uses native Web Crypto API) Several (relies on Node's crypto)
Async/Sync Native Promises (Async/Await) Primarily Callback/Sync
Encryption (JWE) Yes (full support) No (Signing only)
Key Management Excellent (JWK, JWKS support) Basic
Algorithms Modern (EdDSA, RSA-PSS, etc.) Standard (HMAC, RSA)
Bundle Size Lightweight & Tree-shakable Heavier

Summary by CodeRabbit

  • Chores
    • Updated JWT token generation library for improved compatibility and maintainability.

@vercel
Copy link

vercel bot commented Feb 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cli-web-cli Ready Ready Preview, Comment Feb 26, 2026 11:38am

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Walkthrough

The pull request replaces the JWT library dependency from jsonwebtoken to jose, updating the JWT token issuance implementation to use jose's SignJWT API, refactoring client ID handling and timestamp management, and adapting test assertions to accommodate the new library's verification API.

Changes

Cohort / File(s) Summary
Dependency Updates
package.json
Replaced jsonwebtoken with jose in dependencies; removed corresponding @types/jsonwebtoken from devDependencies.
JWT Token Issuance
src/commands/auth/issue-jwt-token.ts
Migrated from jsonwebtoken to jose SignJWT; refactored client ID handling to support explicit "none", explicit client-id, or generated random ID; centralized iat/exp timestamp computation; updated claims to use x-ably prefixed headers (appId, capability, clientId); adjusted output formatting and logging.
Test Validation
test/unit/commands/auth/issue-jwt-token.test.ts
Introduced async verifyToken helper using jose's jwtVerify; updated all token verification calls to await the helper; adjusted assertions for payload typing differences and TTL computation using (exp - iat) cast.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 With jose's swift hand, tokens now sign with grace,
No more jsonwebtoken in this rabbit's place,
SignJWT fluent and claims so fine,
Tests await their verdicts, each one in line,
Libraries swapped, the code hops anew! 🏃‍♂️✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 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: replacing jsonwebtoken with jose. It is specific, concise, and clearly summarizes the primary objective of the PR.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/replace-jsonwebtoken-with-jose

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.

@sacOO7 sacOO7 changed the title Fix: Replaced jsonwebtoken with jose — a modern, ESM-native JWT library Fix: Replaced jsonwebtoken with jose - a modern, ESM-native JWT library Feb 26, 2026
@sacOO7
Copy link
Author

sacOO7 commented Feb 26, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces jsonwebtoken with jose for JWT signing/verification to avoid a Node.js v22+ runtime incompatibility that was breaking installs/manifest generation, while keeping the auth:issue-jwt-token command behavior consistent.

Changes:

  • Switched JWT signing in auth:issue-jwt-token from jsonwebtoken to jose (SignJWT).
  • Updated unit tests to verify issued tokens using jose (jwtVerify) instead of jsonwebtoken.
  • Replaced dependencies accordingly (package.json + pnpm-lock.yaml), removing @types/jsonwebtoken.

Reviewed changes

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

File Description
src/commands/auth/issue-jwt-token.ts Uses jose to sign HS256 JWTs (sets kid, iat, exp, jti) and preserves existing output behavior.
test/unit/commands/auth/issue-jwt-token.test.ts Verifies generated JWTs with jose and updates type assertions for decoded claims.
package.json Swaps jsonwebtoken for jose and removes @types/jsonwebtoken.
pnpm-lock.yaml Lockfile updates reflecting dependency replacement/removal.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@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

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

Inline comments:
In `@src/commands/auth/issue-jwt-token.ts`:
- Around line 98-101: The JWT payload creation in the SignJWT call incorrectly
includes capabilities as an object; change the payload construction in
issue-jwt-token (the SignJWT instantiation that sets "x-ably-capability") to
include JSON.stringify(capabilities) instead of the raw object, and update any
tests that inspect decoded["x-ably-capability"] to JSON.parse that claim before
accessing properties (tests referencing decoded["x-ably-capability"] at the two
failing assertions should be modified to parse the string claim first).

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 040e7df and 571270f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • package.json
  • src/commands/auth/issue-jwt-token.ts
  • test/unit/commands/auth/issue-jwt-token.test.ts

Comment on lines +98 to +101
const token = await new SignJWT({
"x-ably-appId": appId,
"x-ably-capability": capabilities,
...clientIdClaim,
Copy link

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

🌐 Web query:

Ably JWT authentication x-ably-capability claim format specification

💡 Result:

For an Ably JWT (JWT signed with your Ably API key secret), the x-ably-capability claim must be a string whose contents are JSON representing an Ably capability map (resource → list of operations). (ably.com)

Claim format (payload)

{
  "x-ably-capability": "{\"chat:*\": [\"publish\",\"subscribe\"], \"status\": [\"subscribe\",\"history\"]}"
}

x-ably-capability is not a JSON object in the JWT payload; it’s a JSON-encoded string. (ably.com)

Capability map JSON (inside the string)

Shape

{
  "<resource>": ["<operation>", "..."],
  "<resource2>": ["*"]
}

Resources

  • Channel or channel pattern (supports :-segment wildcards like namespace:*, and * for all channels). (ably.com)
  • Queue resource: starts with "[queue]" (e.g. "[queue]myapp-queue"), wildcard rules apply (e.g. "[queue]*"). (ably.com)
  • Metachannel resource: starts with "[meta]" (e.g. "[meta]metaname"), wildcard rules apply (e.g. "[meta]*"). (ably.com)
  • Special resource [*]* matches all channels, all queues, and all metachannels. (ably.com)

Operations
Use any of Ably’s capability operations, e.g. subscribe, publish, presence, history, stats, push-subscribe, push-admin, channel-metadata, etc. (or "*" for all operations). (ably.com)

Validation note

An empty string for x-ably-capability can cause JWT decode/validation failure. (faqs.ably.com)

Citations:


🏁 Script executed:

# Check the current implementation in the file
cat -n src/commands/auth/issue-jwt-token.ts | sed -n '90,110p'

Repository: ably/ably-cli

Length of output: 956


🏁 Script executed:

# Check the test file to see how x-ably-capability is being asserted
cat -n test/unit/commands/auth/issue-jwt-token.test.ts | sed -n '60,80p'

Repository: ably/ably-cli

Length of output: 847


🏁 Script executed:

# Also check lines around 167 mentioned in the review
cat -n test/unit/commands/auth/issue-jwt-token.test.ts | sed -n '160,175p'

Repository: ably/ably-cli

Length of output: 717


Encode x-ably-capability as JSON string, not object.

Line 100 passes capabilities as an object. According to Ably's JWT specification, x-ably-capability must be a JSON-encoded string within the JWT payload. Passing it as an object will cause tokens to fail Ably JWT validation at runtime.

      const token = await new SignJWT({
        "x-ably-appId": appId,
-       "x-ably-capability": capabilities,
+       "x-ably-capability": JSON.stringify(capabilities),
        ...clientIdClaim,
      })

Tests currently expect decoded["x-ably-capability"] as an object (lines 71 and 167), but this masks the bug. After fixing the code, tests must parse the claim string before accessing properties:

-     expect(decoded["x-ably-capability"]).toHaveProperty("chat:*");
+     const capabilityMap = JSON.parse(decoded["x-ably-capability"] as string);
+     expect(capabilityMap).toHaveProperty("chat:*");
📝 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 token = await new SignJWT({
"x-ably-appId": appId,
"x-ably-capability": capabilities,
...clientIdClaim,
const token = await new SignJWT({
"x-ably-appId": appId,
"x-ably-capability": JSON.stringify(capabilities),
...clientIdClaim,
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/auth/issue-jwt-token.ts` around lines 98 - 101, The JWT payload
creation in the SignJWT call incorrectly includes capabilities as an object;
change the payload construction in issue-jwt-token (the SignJWT instantiation
that sets "x-ably-capability") to include JSON.stringify(capabilities) instead
of the raw object, and update any tests that inspect
decoded["x-ably-capability"] to JSON.parse that claim before accessing
properties (tests referencing decoded["x-ably-capability"] at the two failing
assertions should be modified to parse the string claim first).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants