Skip to content

Conversation

@wobsoriano
Copy link
Member

@wobsoriano wobsoriano commented Nov 25, 2025

Description

This PR adds JWT verification support for OAuth access tokens (acceptsToken: 'oauth_token'). Previously, only tokens starting with oat_ are verified as oauth tokens at edge.

  • Used existing JWT functions while extending verifyJwt with a headerType parameter to support OAuth access token JWTs (RFC 9068)
  • Added tests for headerType assertions, and OAuth JWT verifications

Resolves USER-2546

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Accept OAuth-format JWT access tokens and map their payloads into access-token objects.
    • Recognize multiple OAuth JWT header types.
  • Improvements

    • Configurable JWT header-type validation (single or multiple allowed types).
    • Broader token detection & routing to JWT verification.
    • Enhanced verification reporting with an additional failure classification plus optional status/action details.
  • Tests

    • Added fixtures and tests covering OAuth JWT formats, verification flows, and error cases.
  • Chores

    • Release bump recorded in changeset.

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

@changeset-bot
Copy link

changeset-bot bot commented Nov 25, 2025

🦋 Changeset detected

Latest commit: 4ca5379

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

This PR includes changesets to release 11 packages
Name Type
@clerk/backend Minor
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/remix Patch
@clerk/tanstack-react-start Patch
@clerk/testing 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

@vercel
Copy link

vercel bot commented Nov 25, 2025

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

Project Deployment Preview Comments Updated (UTC)
clerk-js-sandbox Ready Ready Preview Comment Dec 4, 2025 5:38pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 25, 2025

Walkthrough

Adds JWT detection and verification for OAuth access tokens, header-type flexibility in JWT assertions/verification, mapping JWT payloads into IdPOAuthAccessToken, new error code/action support, new fixtures/tests, and routes JWT-formatted machine tokens into the OAuth verification flow.

Changes

Cohort / File(s) Summary
Changeset Documentation
\.changeset/swift-sheep-notice.md
Adds changeset marking a minor bump for @clerk/backend and documents added JWT support for the OAuth token type.
OAuth Token Model
packages/backend/src/api/resources/IdPOAuthAccessToken.ts
Adds IdPOAuthAccessToken.fromJwtPayload(payload, clockSkewInMs) and local OAuthJwtPayload mapping JWT claims to token fields; imports JwtPayload.
Error Handling
packages/backend/src/errors.ts
Adds TokenVerificationFailed code; makes MachineTokenVerificationError.status optional and adds action?: TokenVerificationErrorAction; constructor and getFullMessage updated to handle optional status/action.
Fixtures
packages/backend/src/fixtures/index.ts, packages/backend/src/fixtures/machine.ts
Adds mockOAuthAccessTokenJwtPayload, mockSignedOAuthAccessTokenJwt, and mockSignedOAuthAccessTokenJwtApplicationTyp fixtures.
JWT Header Assertion
packages/backend/src/jwt/assertions.ts
assertHeaderType(typ?, allowedTypes = 'JWT') now accepts `string
JWT Verification Options & Tests
packages/backend/src/jwt/verifyJwt.ts, packages/backend/src/jwt/__tests__/*
Adds `VerifyJwtOptions.headerType?: string
Machine Token Detection & Utilities
packages/backend/src/tokens/machine.ts, packages/backend/src/tokens/__tests__/*
Adds JwtFormatRegExp, isJwtFormat(), OAUTH_ACCESS_TOKEN_TYPES, isOAuthJwt(); updates isMachineToken() and getMachineTokenType() to recognize OAuth JWTs; exports new helpers and updates tests.
Request Authentication
packages/backend/src/tokens/request.ts
Replaces isMachineTokenByPrefix() uses with isMachineToken() in token detection and branching; updates imports and conditional logic.
Token Verification (OAuth/JWT path)
packages/backend/src/tokens/verify.ts
Adds internal verifyJwtOAuthToken to decode/verify JWT-formatted OAuth tokens (resolve JWK/PEM, call verifyJwt, map payload to IdPOAuthAccessToken); routes JWT-format tokens into this flow and maps failures to MachineTokenVerificationError.
Token Tests (verify & machine)
packages/backend/src/tokens/__tests__/verify.test.ts, packages/backend/src/tokens/__tests__/machine.test.ts
Adds createOAuthJwt helper; tests isJwtFormat/isOAuthJwt; verifies OAuth JWT flows (valid, invalid typ, alg none, expired, alternate typ handling); updates imports to new fixtures/helpers.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Req as Client Request
    participant Auth as AuthHandler
    participant Machine as MachineUtils
    participant Verify as TokenVerifier
    participant JWT as JWTUtils
    participant Model as IdPOAuthAccessToken
    participant Err as MachineTokenVerificationError

    Req->>Auth: authenticateRequest(token)
    Auth->>Machine: isMachineToken(token)
    alt machine token (JWT-format)
        Machine-->>Auth: true
        Auth->>Verify: verifyMachineAuthToken(token)
        Verify->>Machine: isOAuthJwt(token)?
        alt OAuth JWT
            Machine-->>Verify: true
            Verify->>JWT: decodeJwt(token)
            JWT->>JWT: assertHeaderType(typ, allowedTypes)
            JWT-->>Verify: payload
            Verify->>Model: fromJwtPayload(payload, clockSkewInMs)
            Model-->>Verify: IdP token
            Verify-->>Auth: verified token
        else Not OAuth JWT
            Machine-->>Verify: false
            Verify->>Verify: prefix-based verification (remote JWK/PEM)
        end
    else not machine token
        Machine-->>Auth: false
        Auth->>Verify: session/API-key verification
    end
    alt verification error
        Verify-->>Auth: throw MachineTokenVerificationError (TokenInvalid / TokenVerificationFailed)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus areas:
    • packages/backend/src/tokens/verify.ts — new JWT decode/verify flow, JWK/PEM resolution, and error mapping.
    • packages/backend/src/tokens/machine.ts — regex and JWT detection, token-type resolution.
    • packages/backend/src/tokens/request.ts — branching changes relying on new predicate.
    • packages/backend/src/jwt/assertions.ts and packages/backend/src/jwt/verifyJwt.ts — headerType propagation and updated error messages.
    • Tests/fixtures — correctness of mock JWTs, createJwt/createOAuthJwt helpers, and expanded test coverage.

Poem

🐇 I sniffed a typ of at+jwt,
I twitched my whiskers, checked the bit,
Mapped jti and scope with glee,
Hopped the sig through verify tree,
OAuth tokens now hop home with me.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 title clearly and concisely summarizes the main change: adding JWT support for OAuth token types in the backend.
Linked Issues check ✅ Passed The PR implements all requirements from USER-2546: detects JWT-formatted OAuth access tokens, adds JWT verification support, and eliminates dependency on the /oauth_applications/access_tokens/verify endpoint.
Out of Scope Changes check ✅ Passed All changes directly support JWT OAuth token detection and verification. No unrelated modifications were introduced outside the scope of USER-2546.
✨ 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 rob/user-4076-oat

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 25, 2025

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7308

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@7308

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@7308

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@7308

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@7308

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@7308

@clerk/elements

npm i https://pkg.pr.new/@clerk/elements@7308

@clerk/clerk-expo

npm i https://pkg.pr.new/@clerk/clerk-expo@7308

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@7308

@clerk/express

npm i https://pkg.pr.new/@clerk/express@7308

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@7308

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@7308

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@7308

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@7308

@clerk/clerk-react

npm i https://pkg.pr.new/@clerk/clerk-react@7308

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@7308

@clerk/remix

npm i https://pkg.pr.new/@clerk/remix@7308

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@7308

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@7308

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@7308

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@7308

@clerk/types

npm i https://pkg.pr.new/@clerk/types@7308

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@7308

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@7308

commit: 4ca5379

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

🧹 Nitpick comments (9)
packages/backend/src/api/resources/IdPOAuthAccessToken.ts (2)

47-63: Consider validating required JWT claims before construction.

The jti claim is used as the token id. If jti is missing or empty, the resulting token will have an empty id, which could cause issues in downstream logic that relies on token identification. Consider throwing a TokenVerificationError when required claims are missing rather than silently defaulting to empty strings.

  static fromJwtPayload(payload: JwtPayload, clockSkewInMs = 5000): IdPOAuthAccessToken {
    const oauthPayload = payload as OAuthJwtPayload;

+   if (!oauthPayload.jti) {
+     throw new TokenVerificationError({
+       reason: TokenVerificationErrorReason.TokenVerificationFailed,
+       message: 'Invalid JWT jti claim. Expected a non-empty string.',
+     });
+   }
+
    // Map JWT claims to IdPOAuthAccessToken fields
    return new IdPOAuthAccessToken(
-     oauthPayload.jti ?? '',
+     oauthPayload.jti,
      oauthPayload.client_id ?? '',

Note: The import for TokenVerificationError and TokenVerificationErrorReason would need to be added from ../errors.


5-10: Add JSDoc documentation for the OAuthJwtPayload type.

Per coding guidelines, public APIs and types should be documented with JSDoc. This type extends JwtPayload with OAuth-specific claims and would benefit from documentation explaining each field.

+/**
+ * Extended JWT payload for OAuth 2.0 access tokens per RFC 9068.
+ * @see https://www.rfc-editor.org/rfc/rfc9068.html
+ */
 type OAuthJwtPayload = JwtPayload & {
+  /** JWT ID - unique identifier for the token */
   jti?: string;
+  /** OAuth 2.0 client identifier */
   client_id?: string;
+  /** Space-delimited scope string */
   scope?: string;
+  /** Array of scope values (alternative to space-delimited scope) */
   scp?: string[];
 };
packages/backend/src/tokens/__tests__/verify.test.ts (2)

436-451: Consider removing verbose implementation comments.

These comments explain the testing rationale in detail, which is useful during development but adds noise to the test file. The test description and assertions should be self-explanatory.

    it('verifies JWT with typ application/at+jwt', async () => {
      server.use(
        http.get(
          'https://api.clerk.test/v1/jwks',
          validateHeaders(() => {
            return HttpResponse.json(mockJwks);
          }),
        ),
      );

      const validJtiPayload = {
        ...mockOAuthAccessTokenJwtPayload,
        jti: 'oat_12345678901234567890123456789012',
      };

-      // We mock verifyJwt to ensure we don't hit the real signature check failure (since we use createJwt which signs with a dummy key/algo)
-      // But we want to test headerType validation logic. The real verifyJwt does signature check LAST.
-      // assertHeaderType is called BEFORE signature check.
-      // So if we mock verifyJwt, we skip assertHeaderType inside verifyJwt unless we spy on assertions?
-      // No, VerifyJwtModule.verifyJwt IS the function we call.
-      // If we mock it, we bypass the real logic entirely.
-
-      // To test that verifyOAuthToken accepts 'application/at+jwt', we should let it call verifyJwt.
-      // But verifyJwt will fail signature if we don't provide correct key/signature.
-      // However, if we Mock verifyJwt, we are just testing verifyMachineAuthToken -> verifyOAuthToken flow, not verifyJwt internals.
-
-      // Let's rely on the fact that verifyOAuthToken calls verifyJwt with headerType: ['at+jwt', 'application/at+jwt'].
-      // We verified that call in the first test ("verifies a valid OAuth JWT").
-
-      // But we can add a test case where we simulate a successful verifyJwt call with application/at+jwt.
-
      const spy = vi.spyOn(VerifyJwtModule, 'verifyJwt').mockResolvedValueOnce({

477-491: Add more specific error assertions for invalid JWT format test.

The test only checks that errors is defined. Consider asserting on the specific error code or message to ensure the correct error path is exercised.

    it('handles invalid JWT format', async () => {
      const invalidJwt = 'invalid.jwt.token';
-      // Mock isJwt to return true so it enters verifyOAuthToken,
-      // OR rely on regex. Regex requires 3 parts.
-      // 'invalid.jwt.token' matches 3 parts.
-
-      // But decodeJwt will fail if base64 is bad.

      const result = await verifyMachineAuthToken(invalidJwt, {
        apiUrl: 'https://api.clerk.test',
        secretKey: 'a-valid-key',
      });

      expect(result.errors).toBeDefined();
+      expect(result.errors?.[0].code).toBeDefined();
    });
packages/backend/src/tokens/machine.ts (2)

18-22: Consider exporting OAUTH_ACCESS_TOKEN_TYPES for external consumers.

This constant defines RFC 9068 compliant OAuth access token types and could be useful for consumers who need to validate or generate OAuth JWTs outside of this module.

 /**
  * Valid OAuth 2.0 JWT access token type values per RFC 9068.
  * @see https://www.rfc-editor.org/rfc/rfc9068.html#section-2.1
  */
-const OAUTH_ACCESS_TOKEN_TYPES = ['at+jwt', 'application/at+jwt'] as const;
+export const OAUTH_ACCESS_TOKEN_TYPES = ['at+jwt', 'application/at+jwt'] as const;

24-34: Optimize redundant JWT decoding in machine token detection flow.

Verified: isMachineToken() (request.ts:105) calls isOAuthJwt() which decodes the JWT, then immediately getMachineTokenType() (request.ts:106) calls isOAuthJwt() again on the same token, causing redundant parsing. Consider memoizing the decoded JWT result, combining detection and type extraction, or refactoring to avoid dual decoding in the authentication hot path.

packages/backend/src/tokens/verify.ts (3)

213-227: Redundant try-catch around decodeJwt.

decodeJwt returns a result object with { data, errors } rather than throwing exceptions. The outer try-catch on lines 215-227 is unnecessary since errors are already handled via the result pattern on lines 229-240.

Consider simplifying:

-  if (isJwt(accessToken)) {
-    let decoded: JwtReturnType<Jwt, TokenVerificationError>;
-    try {
-      decoded = decodeJwt(accessToken);
-    } catch (e) {
-      return {
-        tokenType: TokenType.OAuthToken,
-        errors: [
-          new MachineTokenVerificationError({
-            code: MachineTokenVerificationErrorCode.TokenInvalid,
-            message: (e as Error).message,
-          }),
-        ],
-      };
-    }
-
-    const { data: decodedResult, errors } = decoded;
+  if (isJwt(accessToken)) {
+    const { data: decodedResult, errors } = decodeJwt(accessToken);

282-294: Hardcoded jti format validation.

The regex /^oat_[0-9A-Za-z]{32}$/ is hardcoded here. Consider extracting this pattern to a shared constant alongside OAUTH_TOKEN_PREFIX in machine.ts for consistency and maintainability.


350-361: Remove duplicate isJwt and reuse exported isJwtFormat from machine.ts.

Both isJwt in verify.ts (lines 359-361) and isJwtFormat in machine.ts are identical—same regex pattern and logic. Replace the duplicate function and regex with an import of isJwtFormat:

-import { API_KEY_PREFIX, M2M_TOKEN_PREFIX, OAUTH_TOKEN_PREFIX } from './machine';
+import { API_KEY_PREFIX, isJwtFormat, M2M_TOKEN_PREFIX, OAUTH_TOKEN_PREFIX } from './machine';
...
-const JwtFormatRegExp = /^[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+$/;
-
-function isJwt(token: string): boolean {
-  return JwtFormatRegExp.test(token);
-}

Then update lines 213 and 350 to use isJwtFormat(...) instead of isJwt(...).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 b248601 and e86c3ab.

📒 Files selected for processing (11)
  • .changeset/swift-sheep-notice.md (1 hunks)
  • packages/backend/src/api/resources/IdPOAuthAccessToken.ts (2 hunks)
  • packages/backend/src/errors.ts (2 hunks)
  • packages/backend/src/fixtures/index.ts (1 hunks)
  • packages/backend/src/jwt/assertions.ts (1 hunks)
  • packages/backend/src/jwt/verifyJwt.ts (2 hunks)
  • packages/backend/src/tokens/__tests__/machine.test.ts (2 hunks)
  • packages/backend/src/tokens/__tests__/verify.test.ts (2 hunks)
  • packages/backend/src/tokens/machine.ts (4 hunks)
  • packages/backend/src/tokens/request.ts (4 hunks)
  • packages/backend/src/tokens/verify.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/jwt/assertions.ts
  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/errors.ts
  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/request.ts
  • packages/backend/src/tokens/machine.ts
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/api/resources/IdPOAuthAccessToken.ts
  • packages/backend/src/tokens/verify.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/jwt/assertions.ts
  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/errors.ts
  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/request.ts
  • packages/backend/src/tokens/machine.ts
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/api/resources/IdPOAuthAccessToken.ts
  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/jwt/assertions.ts
  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/errors.ts
  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/request.ts
  • packages/backend/src/tokens/machine.ts
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/api/resources/IdPOAuthAccessToken.ts
  • packages/backend/src/tokens/verify.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/jwt/assertions.ts
  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/errors.ts
  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/request.ts
  • packages/backend/src/tokens/machine.ts
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/api/resources/IdPOAuthAccessToken.ts
  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/jwt/assertions.ts
  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/errors.ts
  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/request.ts
  • packages/backend/src/tokens/machine.ts
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/api/resources/IdPOAuthAccessToken.ts
  • packages/backend/src/tokens/verify.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/jwt/assertions.ts
  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/errors.ts
  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/request.ts
  • packages/backend/src/tokens/machine.ts
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/api/resources/IdPOAuthAccessToken.ts
  • packages/backend/src/tokens/verify.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/jwt/assertions.ts
  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/errors.ts
  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/request.ts
  • packages/backend/src/tokens/machine.ts
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/api/resources/IdPOAuthAccessToken.ts
  • packages/backend/src/tokens/verify.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

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

**/*.{test,spec}.{ts,tsx,js,jsx}: Unit tests are required for all new functionality
Verify proper error handling and edge cases
Include tests for all new features

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
**/*.{test,spec,e2e}.{ts,tsx,js,jsx}

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

Use real Clerk instances for integration tests

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
**/index.ts

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

Avoid barrel files (index.ts re-exports) as they can cause circular dependencies

Files:

  • packages/backend/src/fixtures/index.ts
🧬 Code graph analysis (5)
packages/backend/src/jwt/assertions.ts (1)
packages/backend/src/errors.ts (5)
  • TokenVerificationError (41-69)
  • TokenVerificationErrorAction (30-36)
  • TokenVerificationErrorAction (38-39)
  • TokenVerificationErrorReason (9-25)
  • TokenVerificationErrorReason (27-28)
packages/backend/src/tokens/__tests__/verify.test.ts (6)
packages/backend/src/mock-server.ts (1)
  • server (6-6)
packages/backend/src/fixtures/index.ts (2)
  • mockOAuthAccessTokenJwtPayload (36-46)
  • createJwt (49-60)
.typedoc/custom-theme.mjs (1)
  • result (171-171)
packages/backend/src/tokens/verify.ts (1)
  • verifyMachineAuthToken (340-355)
packages/backend/src/internal.ts (1)
  • verifyMachineAuthToken (57-57)
packages/backend/src/api/resources/IdPOAuthAccessToken.ts (1)
  • IdPOAuthAccessToken (12-65)
packages/backend/src/tokens/request.ts (1)
packages/backend/src/tokens/machine.ts (1)
  • isMachineToken (56-58)
packages/backend/src/tokens/machine.ts (2)
packages/backend/src/jwt/verifyJwt.ts (1)
  • decodeJwt (45-95)
packages/backend/src/internal.ts (1)
  • isMachineTokenByPrefix (59-59)
packages/backend/src/jwt/verifyJwt.ts (3)
packages/backend/src/jwt/types.ts (1)
  • JwtReturnType (3-11)
packages/backend/src/errors.ts (1)
  • TokenVerificationError (41-69)
packages/backend/src/jwt/assertions.ts (1)
  • assertHeaderType (50-63)
⏰ 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). (33)
  • GitHub Check: Integration Tests (nextjs, chrome, 14)
  • GitHub Check: Integration Tests (expo-web, chrome)
  • GitHub Check: Integration Tests (machine, chrome, RQ)
  • GitHub Check: Integration Tests (quickstart, chrome, 16)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (sessions, chrome)
  • GitHub Check: Integration Tests (quickstart, chrome, 15)
  • GitHub Check: Integration Tests (billing, chrome, RQ)
  • GitHub Check: Integration Tests (nextjs, chrome, 16)
  • GitHub Check: Integration Tests (handshake:staging, chrome)
  • GitHub Check: Integration Tests (vue, chrome)
  • GitHub Check: Integration Tests (machine, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 15, RQ)
  • GitHub Check: Integration Tests (custom, chrome)
  • GitHub Check: Integration Tests (react-router, chrome)
  • GitHub Check: Integration Tests (sessions:staging, chrome)
  • GitHub Check: Integration Tests (nuxt, chrome)
  • GitHub Check: Integration Tests (elements, chrome)
  • GitHub Check: Integration Tests (handshake, chrome)
  • GitHub Check: Integration Tests (tanstack-react-start, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (generic, chrome)
  • GitHub Check: Integration Tests (localhost, chrome)
  • GitHub Check: Integration Tests (astro, chrome)
  • GitHub Check: Integration Tests (ap-flows, chrome)
  • GitHub Check: Integration Tests (express, chrome)
  • GitHub Check: Publish with pkg-pr-new
  • GitHub Check: Unit Tests (22, shared, clerk-js, RQ)
  • GitHub Check: Unit Tests (22, **)
  • GitHub Check: Static analysis
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (12)
packages/backend/src/tokens/__tests__/machine.test.ts (1)

98-139: Good test coverage for new JWT utilities.

The tests comprehensively cover:

  • Valid/invalid JWT format detection with boundary cases
  • OAuth JWT type detection for both RFC 9068 compliant typ values (at+jwt and application/at+jwt)
  • Edge cases like malformed JWT strings
packages/backend/src/jwt/assertions.ts (1)

50-63: LGTM!

The enhanced assertHeaderType function maintains backward compatibility with the default 'JWT' value while supporting multiple allowed header types for OAuth JWT verification. The implementation correctly normalizes the input to an array and provides a clear error message.

packages/backend/src/jwt/verifyJwt.ts (1)

122-127: LGTM!

The headerType option is well-documented with an RFC reference, properly typed, and cleanly integrated into the verification flow.

packages/backend/src/errors.ts (1)

73-110: LGTM!

The error handling extensions are well-structured:

  • New TokenVerificationFailed code for OAuth JWT verification errors
  • Optional status and action fields maintain backward compatibility
  • The getFullMessage() gracefully handles missing status with 'n/a'
packages/backend/src/tokens/machine.ts (1)

50-58: Good implementation of combined machine token detection.

The isMachineToken function cleanly combines prefix-based detection with OAuth JWT recognition, properly extending machine token handling to support JWT-format OAuth tokens.

packages/backend/src/tokens/request.ts (4)

17-17: LGTM!

The import update correctly reflects the new isMachineToken predicate which now includes OAuth JWT detection alongside prefix-based machine token detection.


101-113: LGTM!

The function correctly uses isMachineToken to determine token type, which now properly handles both prefix-based machine tokens and OAuth JWTs.


699-732: LGTM!

The guard at line 707 correctly rejects non-machine tokens when the endpoint expects machine tokens. The updated predicate ensures OAuth JWTs are now recognized as valid machine tokens and routed to verifyMachineAuthToken.


734-774: LGTM!

The authenticateAnyRequestWithTokenInHeader function correctly uses isMachineToken at line 742 to route OAuth JWTs through the machine token verification path, while regular session JWTs continue through verifyToken.

packages/backend/src/tokens/verify.ts (1)

264-268: Good addition: JWT header type validation for OAuth tokens.

The headerType: ['at+jwt', 'application/at+jwt'] option correctly validates that the JWT header type conforms to RFC 9068 for access tokens.

packages/backend/src/fixtures/index.ts (2)

48-60: LGTM!

The createJwt helper is well-designed with sensible defaults and proper base64url encoding. The type definition allows flexible override of header, payload, and signature.


62-65: LGTM!

The mockOAuthAccessTokenJwt correctly uses typ: 'at+jwt' header which aligns with the RFC 9068 validation in verifyOAuthToken.

@wobsoriano wobsoriano changed the title feat(backend): Add support to JWTs in oauth token type feat(backend): Add support for JWTs in OAuth token type Nov 25, 2025
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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 (2)
packages/backend/src/tokens/__tests__/verify.test.ts (2)

402-421: Consider mocking verifyJwt for deterministic type validation testing.

Unlike other tests in this block, this test doesn't mock verifyJwt. The test relies on the real verification flow, but the mock JWT signature likely won't pass cryptographic verification, potentially failing before the headerType check. Consider mocking verifyJwt to return an error specifically for the type mismatch to ensure this test validates the intended behavior.

     it('fails if JWT type is not at+jwt or application/at+jwt', async () => {
-      server.use(
-        http.get(
-          'https://api.clerk.test/v1/jwks',
-          validateHeaders(() => {
-            return HttpResponse.json(mockJwks);
-          }),
-        ),
-      );
-
+      vi.spyOn(VerifyJwtModule, 'verifyJwt').mockResolvedValueOnce({
+        errors: [{ message: 'Invalid JWT type "JWT". Expected "at+jwt" or "application/at+jwt".' }],
+      });
+
       const oauthJwt = createOAuthJwt(mockOAuthAccessTokenJwtPayload, 'JWT'); // Wrong type

       const result = await verifyMachineAuthToken(oauthJwt, {
         apiUrl: 'https://api.clerk.test',
         secretKey: 'a-valid-key',
       });

       expect(result.errors).toBeDefined();
       expect(result.errors?.[0].message).toContain('Invalid JWT type');
     });

460-469: Strengthen the assertion to verify the specific error.

The current assertion only checks that errors is defined. Consider validating the error code and/or message for more robust testing, similar to other error cases in this file.

     it('handles invalid JWT format', async () => {
       const invalidJwt = 'invalid.jwt.token';

       const result = await verifyMachineAuthToken(invalidJwt, {
         apiUrl: 'https://api.clerk.test',
         secretKey: 'a-valid-key',
       });

       expect(result.errors).toBeDefined();
+      expect(result.errors?.[0].code).toBe('token-invalid');
     });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 e86c3ab and 1b6de49.

📒 Files selected for processing (3)
  • .changeset/swift-sheep-notice.md (1 hunks)
  • packages/backend/src/tokens/__tests__/verify.test.ts (2 hunks)
  • packages/backend/src/tokens/verify.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/swift-sheep-notice.md
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

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

**/*.{test,spec}.{ts,tsx,js,jsx}: Unit tests are required for all new functionality
Verify proper error handling and edge cases
Include tests for all new features

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
**/*.{test,spec,e2e}.{ts,tsx,js,jsx}

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

Use real Clerk instances for integration tests

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
🧬 Code graph analysis (1)
packages/backend/src/tokens/__tests__/verify.test.ts (4)
packages/backend/src/fixtures/index.ts (2)
  • mockOAuthAccessTokenJwtPayload (36-46)
  • createJwt (49-60)
packages/backend/src/mock-server.ts (2)
  • server (6-6)
  • validateHeaders (9-47)
packages/backend/src/tokens/verify.ts (1)
  • verifyMachineAuthToken (340-355)
packages/backend/src/api/resources/IdPOAuthAccessToken.ts (1)
  • IdPOAuthAccessToken (12-65)
⏰ 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). (32)
  • GitHub Check: Integration Tests (quickstart, chrome, 15)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (react-router, chrome)
  • GitHub Check: Integration Tests (quickstart, chrome, 16)
  • GitHub Check: Integration Tests (vue, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 16)
  • GitHub Check: Integration Tests (machine, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 14)
  • GitHub Check: Integration Tests (machine, chrome, RQ)
  • GitHub Check: Integration Tests (nextjs, chrome, 15, RQ)
  • GitHub Check: Integration Tests (billing, chrome, RQ)
  • GitHub Check: Integration Tests (custom, chrome)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (tanstack-react-start, chrome)
  • GitHub Check: Integration Tests (nuxt, chrome)
  • GitHub Check: Integration Tests (expo-web, chrome)
  • GitHub Check: Integration Tests (handshake:staging, chrome)
  • GitHub Check: Integration Tests (sessions, chrome)
  • GitHub Check: Integration Tests (ap-flows, chrome)
  • GitHub Check: Integration Tests (astro, chrome)
  • GitHub Check: Integration Tests (sessions:staging, chrome)
  • GitHub Check: Integration Tests (elements, chrome)
  • GitHub Check: Integration Tests (handshake, chrome)
  • GitHub Check: Integration Tests (generic, chrome)
  • GitHub Check: Integration Tests (localhost, chrome)
  • GitHub Check: Integration Tests (express, chrome)
  • GitHub Check: Publish with pkg-pr-new
  • GitHub Check: Unit Tests (22, shared, clerk-js, RQ)
  • GitHub Check: Unit Tests (22, **)
  • GitHub Check: Static analysis
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (10)
packages/backend/src/tokens/__tests__/verify.test.ts (4)

11-20: LGTM! Well-structured test helper.

The helper function is appropriately scoped to the test file and provides a clean API for creating test JWTs with configurable typ header values.


330-373: LGTM! Comprehensive test for valid OAuth JWT verification.

The test correctly validates the token structure, data fields, and ensures verifyJwt is called with the expected headerType values.


375-400: LGTM!

Correctly tests the jti validation logic with a mock that bypasses signature verification.


423-458: LGTM!

Good coverage for the alternative application/at+jwt typ value.

packages/backend/src/tokens/verify.ts (6)

3-5: LGTM!

Import changes correctly add the Jwt type and adjust import structure for IdPOAuthAccessToken (now imported as a value for its static method usage).


213-240: LGTM!

The JWT decoding and error handling follows the established pattern. The try-catch provides a defensive layer even though decodeJwt returns a result object.


242-262: LGTM!

JWK resolution logic correctly follows the established pattern from verifyToken.


264-280: LGTM!

Correctly validates both at+jwt and application/at+jwt typ values per RFC 9068.


296-309: LGTM!

Token creation and error handling are correct. The clockSkewInMs parameter is properly passed to align expiration calculations.


350-352: LGTM!

JWT-format tokens are correctly routed to OAuth verification as a fallback after prefix checks.

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

♻️ Duplicate comments (1)
packages/backend/src/tokens/verify.ts (1)

281-295: Confirm OAuth JWT jti format assumptions before finalizing the regex

The jti validation currently accepts any oat_-prefixed alphanumeric string:

if (!jti || !/^oat_[0-9A-Za-z]+$/.test(jti)) {
  // ...
}

A previous review noted that production OAuth access token IDs may be fixed-length (e.g. exactly 32 chars after oat_), and recommended tightening this regex accordingly, along with adjusting the default fixture value. Before merging, please double‑check the actual IdP OAuth access token ID format and decide whether this regex should enforce a fixed length (e.g. {32}) to match real IDs, or if variable length is intentional.

🧹 Nitpick comments (2)
packages/backend/src/tokens/__tests__/verify.test.ts (1)

329-460: JWT OAuth tests cover key paths; consider tightening the invalid-format assertion

The new verifyOAuthToken with JWT suite nicely exercises:

  • happy path (including headerType propagation),
  • invalid jti,
  • invalid typ,
  • alternate typ (application/at+jwt),
  • and structurally invalid JWTs.

For the "handles invalid JWT format" test, you currently only assert that result.errors is defined. If you want stronger regression protection, you could also assert the machine error code (e.g. token-invalid) or part of the message, so future changes to error mapping don’t silently weaken guarantees.

packages/backend/src/tokens/verify.ts (1)

212-308: Ensure error returns in the JWT OAuth path consistently populate data

The new JWT-specific branch in verifyOAuthToken is structurally sound: it:

  • guards with isJwtFormat,
  • decodes the JWT with a defensive try/catch,
  • resolves the JWK using jwtKey or secretKey,
  • reuses verifyJwt with headerType: ['at+jwt', 'application/at+jwt'],
  • validates jti before mapping via IdPOAuthAccessToken.fromJwtPayload.

One small inconsistency: several error paths in this branch return only { tokenType, errors } without data, while other helpers (like handleClerkAPIError) always set data: undefined on failures. For a discriminated MachineTokenReturnType, consistently including data: undefined in all error cases makes the runtime shape predictable and avoids subtle checks on the presence of the data property.

You can align these branches by populating data: undefined wherever you return errors, e.g.:

-      return {
-        tokenType: TokenType.OAuthToken,
-        errors: [
+      return {
+        data: undefined,
+        tokenType: TokenType.OAuthToken,
+        errors: [
           new MachineTokenVerificationError({
             code: MachineTokenVerificationErrorCode.TokenInvalid,
             message: (e as Error).message,
           }),
         ],
       };

and similarly for the other error returns in this JWT block (errors from decodeJwt, JWK resolution failures, verifyJwt failures, invalid jti, and the outer catch).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 1b6de49 and bf6d6e2.

📒 Files selected for processing (2)
  • packages/backend/src/tokens/__tests__/verify.test.ts (2 hunks)
  • packages/backend/src/tokens/verify.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

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

**/*.{test,spec}.{ts,tsx,js,jsx}: Unit tests are required for all new functionality
Verify proper error handling and edge cases
Include tests for all new features

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
**/*.{test,spec,e2e}.{ts,tsx,js,jsx}

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

Use real Clerk instances for integration tests

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/tokens/verify.ts
🧬 Code graph analysis (1)
packages/backend/src/tokens/verify.ts (4)
packages/backend/src/tokens/machine.ts (1)
  • isJwtFormat (14-16)
packages/backend/src/jwt/verifyJwt.ts (2)
  • decodeJwt (45-95)
  • verifyJwt (129-187)
packages/backend/src/tokens/tokenTypes.ts (2)
  • TokenType (1-6)
  • TokenType (11-11)
packages/backend/src/tokens/keys.ts (2)
  • loadClerkJwkFromPem (52-86)
  • loadClerkJWKFromRemote (131-173)
⏰ 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). (5)
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
packages/backend/src/tokens/__tests__/verify.test.ts (1)

5-20: JWT fixtures import and createOAuthJwt helper look good

Localizing the OAuth JWT builder in the test file and parameterizing typ gives you flexible coverage without coupling tests too tightly to fixtures. Defaulting the payload to mockOAuthAccessTokenJwtPayload is reasonable here since the tests don’t mutate it.

packages/backend/src/tokens/verify.ts (2)

2-5: Type imports and value imports are aligned with shared types usage

Switching to @clerk/shared/types for Jwt, JwtPayload, and Simplify, and importing IdPOAuthAccessToken as a value from ../api, matches the guideline to prefer shared types and keeps tree‑shaking friendly exports.

Also applies to: 18-18


349-351: JWT-format routing in verifyMachineAuthToken is consistent with OAuth JWT support

Adding the isJwtFormat branch here:

if (isJwtFormat(token)) {
  return verifyOAuthToken(token, options);
}

makes verifyMachineAuthToken handle both opaque OAuth IDs (oat_…) and bare OAuth JWTs. Given the earlier prefix checks for M2M, OAuth, and API keys, this shouldn’t interfere with existing machine token flows and keeps the new JWT behavior nicely encapsulated in verifyOAuthToken.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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 (2)
packages/backend/src/fixtures/index.ts (1)

48-66: Tighten CreateJwt option types to avoid any and clarify API surface

The createJwt logic is good, but the current signature uses any for header and payload, which goes against the repo’s TypeScript guidelines and reduces type safety. You can keep the same runtime behavior while improving typings with a small refactor, e.g.:

-type CreateJwt = (opts?: { header?: any; payload?: any; signature?: string }) => string;
-export const createJwt: CreateJwt = ({ header, payload, signature = mockJwtSignature } = {}) => {
+type CreateJwtOptions = {
+  header?: Record<string, unknown>;
+  payload?: Record<string, unknown>;
+  signature?: string;
+};
+
+type CreateJwt = (opts?: CreateJwtOptions) => string;
+
+export const createJwt: CreateJwt = ({ header, payload, signature = mockJwtSignature } = {}) => {
   const encoder = new TextEncoder();

This keeps the helper flexible for tests while avoiding any and making the API a bit clearer. As per coding guidelines, this better aligns with the “no any types” and “use proper type annotations” recommendations.

After adjusting the types, please run the existing TypeScript build/tests (e.g., pnpm build or pnpm test) to confirm there are no downstream type errors where createJwt is used.

packages/backend/src/tokens/verify.ts (1)

214-226: Remove redundant try-catch around decodeJwt.

The decodeJwt function returns errors via JwtReturnType<Jwt, TokenVerificationError> and doesn't throw exceptions (see packages/backend/src/jwt/verifyJwt.ts). The error handling at lines 228-239 already covers the error case. The try-catch here only catches unexpected runtime errors, adding unnecessary nesting.

Apply this diff to simplify the error handling:

-    let decoded: JwtReturnType<Jwt, TokenVerificationError>;
-    try {
-      decoded = decodeJwt(accessToken);
-    } catch (e) {
-      return {
-        tokenType: TokenType.OAuthToken,
-        errors: [
-          new MachineTokenVerificationError({
-            code: MachineTokenVerificationErrorCode.TokenInvalid,
-            message: (e as Error).message,
-          }),
-        ],
-      };
-    }
-
-    const { data: decodedResult, errors } = decoded;
+    const { data: decodedResult, errors } = decodeJwt(accessToken);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 bf6d6e2 and 41b1c73.

📒 Files selected for processing (2)
  • packages/backend/src/fixtures/index.ts (1 hunks)
  • packages/backend/src/tokens/verify.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/verify.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/verify.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/verify.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/verify.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/fixtures/index.ts
  • packages/backend/src/tokens/verify.ts
**/index.ts

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

Avoid barrel files (index.ts re-exports) as they can cause circular dependencies

Files:

  • packages/backend/src/fixtures/index.ts
🧬 Code graph analysis (1)
packages/backend/src/tokens/verify.ts (5)
packages/backend/src/tokens/machine.ts (2)
  • isJwtFormat (14-16)
  • OAUTH_TOKEN_PREFIX (7-7)
packages/backend/src/jwt/types.ts (1)
  • JwtReturnType (3-11)
packages/backend/src/jwt/verifyJwt.ts (2)
  • decodeJwt (45-95)
  • verifyJwt (129-187)
packages/backend/src/tokens/tokenTypes.ts (2)
  • TokenType (1-6)
  • TokenType (11-11)
packages/backend/src/tokens/keys.ts (2)
  • loadClerkJwkFromPem (52-86)
  • loadClerkJWKFromRemote (131-173)
⏰ 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). (5)
  • GitHub Check: Build Packages
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (6)
packages/backend/src/fixtures/index.ts (1)

36-46: OAuth access token JWT payload fixture looks consistent

The payload structure looks solid for an OAuth access token JWT: it reuses the base claims, overrides iss/sub, adds client_id, scope, and jti, and keeps nbf < iat < exp with a short-lived TTL. The jti value now conforms to the expected oat_ + 32‑character suffix format used in verification logic.

packages/backend/src/tokens/verify.ts (5)

2-2: LGTM: Import changes are correct.

The import modifications appropriately support the JWT verification feature. Jwt is correctly imported as a type, IdPOAuthAccessToken is imported as a value (required for the .fromJwtPayload() call), and isJwtFormat is added for routing logic.

Also applies to: 4-4, 18-18


245-279: LGTM: Key resolution and JWT verification logic.

The key resolution follows the same pattern as verifyToken (lines 117-132), ensuring consistency. The headerType validation with ['at+jwt', 'application/at+jwt'] correctly enforces RFC 9068 OAuth 2.0 Access Token JWT format requirements. Error handling appropriately wraps verification failures with MachineTokenVerificationError.


295-297: LGTM: Token creation from JWT payload.

The call to IdPOAuthAccessToken.fromJwtPayload(payload, options.clockSkewInMs) correctly maps the verified JWT payload to the token structure. The timestamp unit issue mentioned in past reviews was addressed in commit bc57e96.


343-343: LGTM: Enhanced routing logic for JWT-format OAuth tokens.

Adding isJwtFormat(token) to the routing condition correctly handles OAuth tokens in JWT format (not just prefix-based tokens). This enables the new JWT verification path while maintaining backward compatibility with existing prefix-based OAuth tokens.


212-309: Comprehensive error handling in JWT verification flow.

The error handling throughout the new JWT verification logic is well-structured with specific error codes for each failure scenario (decode, key resolution, verification, jti validation). The catch-all handler at lines 298-308 appropriately wraps unexpected errors, and all error returns maintain a consistent structure matching existing token verification functions.

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

🧹 Nitpick comments (3)
packages/backend/src/tokens/__tests__/verify.test.ts (2)

5-7: Consider restoring verifyJwt spies between tests to avoid cross-test leakage

Each test creates a new vi.spyOn(VerifyJwtModule, 'verifyJwt'), but the spies are never restored. While using mockResolvedValueOnce means later calls still hit the real implementation, keeping the spy installed can leak into other test files sharing the same module cache. Adding vi.restoreAllMocks() in an afterEach for this file (or spy.mockRestore() in each test) would make the isolation explicit without changing behavior.

Please re-run the suite with vi.restoreAllMocks() enabled to confirm no hidden coupling on the spied function.


11-20: Helper is solid; consider reusing the existing KID constant instead of duplicating it

createOAuthJwt is a nice focused helper and the typ union matches the test cases. To avoid a hard-coded KID string that can drift from mockJwtHeader.kid, you could read it from mockJwtHeader (or omit it entirely and let createJwt’s default header provide it). This keeps fixtures consistent if the header KID ever changes.

packages/backend/src/fixtures/index.ts (1)

48-60: Tighten CreateJwt parameter types instead of using any

createJwt is a handy shared helper, but the header?: any; payload?: any; in CreateJwt are looser than necessary. You can avoid any while still keeping the fixture flexible by using a simple index signature type, e.g.:

-type CreateJwt = (opts?: { header?: any; payload?: any; signature?: string }) => string;
+interface JwtFixtureHeader {
+  [key: string]: unknown;
+}
+
+interface JwtFixturePayload {
+  [key: string]: unknown;
+}
+
+type CreateJwt = (opts?: {
+  header?: JwtFixtureHeader;
+  payload?: JwtFixturePayload;
+  signature?: string;
+}) => string;

This keeps the helper ergonomics the same but respects the “no any without justification” guideline.

As per coding guidelines, avoiding any improves type safety even in test fixtures.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 41b1c73 and fed6afb.

📒 Files selected for processing (2)
  • packages/backend/src/fixtures/index.ts (1 hunks)
  • packages/backend/src/tokens/__tests__/verify.test.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/fixtures/index.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/fixtures/index.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/fixtures/index.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/fixtures/index.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/fixtures/index.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

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

**/*.{test,spec}.{ts,tsx,js,jsx}: Unit tests are required for all new functionality
Verify proper error handling and edge cases
Include tests for all new features

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/fixtures/index.ts
**/*.{test,spec,e2e}.{ts,tsx,js,jsx}

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

Use real Clerk instances for integration tests

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/tokens/__tests__/verify.test.ts
  • packages/backend/src/fixtures/index.ts
**/index.ts

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

Avoid barrel files (index.ts re-exports) as they can cause circular dependencies

Files:

  • packages/backend/src/fixtures/index.ts
🧬 Code graph analysis (1)
packages/backend/src/tokens/__tests__/verify.test.ts (4)
packages/backend/src/fixtures/index.ts (3)
  • mockOAuthAccessTokenJwtPayload (36-46)
  • createJwt (49-60)
  • mockJwks (78-80)
packages/backend/src/mock-server.ts (2)
  • server (6-6)
  • validateHeaders (9-47)
packages/backend/src/tokens/verify.ts (1)
  • verifyMachineAuthToken (339-351)
packages/backend/src/api/resources/IdPOAuthAccessToken.ts (1)
  • IdPOAuthAccessToken (12-65)
⏰ 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). (23)
  • GitHub Check: Integration Tests (nextjs, chrome, 16)
  • GitHub Check: Integration Tests (quickstart, chrome, 15)
  • GitHub Check: Integration Tests (localhost, chrome)
  • GitHub Check: Integration Tests (quickstart, chrome, 16)
  • GitHub Check: Integration Tests (billing, chrome)
  • GitHub Check: Integration Tests (nextjs, chrome, 14)
  • GitHub Check: Integration Tests (machine, chrome, RQ)
  • GitHub Check: Integration Tests (nextjs, chrome, 15, RQ)
  • GitHub Check: Integration Tests (nextjs, chrome, 15)
  • GitHub Check: Integration Tests (custom, chrome)
  • GitHub Check: Integration Tests (billing, chrome, RQ)
  • GitHub Check: Integration Tests (machine, chrome)
  • GitHub Check: Integration Tests (react-router, chrome)
  • GitHub Check: Integration Tests (sessions, chrome)
  • GitHub Check: Integration Tests (expo-web, chrome)
  • GitHub Check: Integration Tests (astro, chrome)
  • GitHub Check: Integration Tests (sessions:staging, chrome)
  • GitHub Check: Integration Tests (ap-flows, chrome)
  • GitHub Check: Integration Tests (generic, chrome)
  • GitHub Check: Integration Tests (elements, chrome)
  • GitHub Check: Unit Tests (22, **)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
packages/backend/src/tokens/__tests__/verify.test.ts (1)

329-460: New OAuth JWT tests give good coverage of the JWT-based OAuth verification flow

The verifyOAuthToken with JWT suite cleanly exercises:

  • happy-path JWT verification (including IdPOAuthAccessToken mapping),
  • invalid jti handling,
  • invalid typ rejection,
  • support for both at+jwt and application/at+jwt,
  • and malformed JWTs.

The use of vi.spyOn(VerifyJwtModule, 'verifyJwt') to isolate headerType wiring and payload mapping is appropriate, and the expectations on headerType align with the new behavior.

packages/backend/src/fixtures/index.ts (2)

36-46: OAuth JWT payload fixture matches the verification and mapping requirements

mockOAuthAccessTokenJwtPayload cleanly extends mockJwtPayload with iss, sub, client_id, scope, and a well-formed jti, plus adjusted exp/iat/nbf. This lines up with the expectations in IdPOAuthAccessToken.fromJwtPayload (including scope → scopes array splitting) and the jti format validation used in the OAuth token verification logic.


62-65: mockOAuthAccessTokenJwt helper is a good reusable fixture for OAuth JWT tests

Exporting mockOAuthAccessTokenJwt built via createJwt with typ: 'at+jwt' and the shared OAuth payload is useful for tests that don’t need to vary header or claims. It keeps JWT construction consistent with other fixtures and centralizes the payload setup.

Copy link
Contributor

@jfoshee jfoshee left a comment

Choose a reason for hiding this comment

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

Overall this looks good to me. Personally I would move the new block of code in verifyOAuthToken to a separate function like

if (isJwtFormat(accessToken)) {
  return verifyJwtAccessToken(...);
}

For approval I defer to the JS team. Thanks for the quick work! 🚀

Co-authored-by: Jacob Foshee <jacob.foshee@clerk.dev>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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

🧹 Nitpick comments (2)
packages/backend/src/tokens/machine.ts (2)

18-23: Consider marking OAUTH_ACCESS_TOKEN_TYPES as readonly with as const

Since these are fixed RFC-defined values, you can tighten the type and prevent accidental mutation with a const assertion:

-export const OAUTH_ACCESS_TOKEN_TYPES = ['at+jwt', 'application/at+jwt'];
+export const OAUTH_ACCESS_TOKEN_TYPES = ['at+jwt', 'application/at+jwt'] as const;

This also gives you a literal union type for (typeof OAUTH_ACCESS_TOKEN_TYPES)[number], aligning with the guidelines on readonly data and as const.


24-42: isOAuthJwt logic is sound; optional refinement for header.typ type‑guard

The control flow (format check → safe decode with try/catch → header typ membership) correctly identifies RFC 9068 OAuth JWT access tokens without attempting verification here.

For slightly clearer typing and input validation, you could explicitly guard the header type before the includes call:

 export function isOAuthJwt(token: string): boolean {
   if (!isJwtFormat(token)) {
     return false;
   }
   try {
-    const { data, errors } = decodeJwt(token);
-    return !errors && !!data && OAUTH_ACCESS_TOKEN_TYPES.includes(data.header.typ as (typeof OAUTH_ACCESS_TOKEN_TYPES)[number]);
+    const { data, errors } = decodeJwt(token);
+    if (errors || !data) {
+      return false;
+    }
+    const typ = data.header?.typ;
+    if (typeof typ !== 'string') {
+      return false;
+    }
+    return OAUTH_ACCESS_TOKEN_TYPES.includes(typ as (typeof OAUTH_ACCESS_TOKEN_TYPES)[number]);
   } catch {
     return false;
   }
 }

This makes the intent explicit and avoids relying on assertions around data.header.typ.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 b54b14a and 9ceae69.

📒 Files selected for processing (2)
  • packages/backend/src/fixtures/index.ts (1 hunks)
  • packages/backend/src/tokens/machine.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/backend/src/fixtures/index.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/tokens/machine.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/tokens/machine.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/tokens/machine.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/tokens/machine.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/tokens/machine.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/tokens/machine.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/tokens/machine.ts
🧬 Code graph analysis (1)
packages/backend/src/tokens/machine.ts (1)
packages/backend/src/jwt/verifyJwt.ts (1)
  • decodeJwt (45-95)
⏰ 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). (4)
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (4)
packages/backend/src/tokens/machine.ts (4)

1-1: Reuse of decodeJwt here is appropriate

Importing decodeJwt directly into this module to inspect the header for classification keeps JWT parsing logic centralized and avoids duplicating decode logic. No issues here.


12-16: JWT format helper looks correct and narrowly scoped

The regexp and isJwtFormat helper correctly implement a quick shape check for three base64url-ish segments before doing a full decode. This is a good, cheap guard in front of decodeJwt.


58-66: Machine token detection now correctly includes OAuth JWTs

Extending isMachineToken to short‑circuit on known prefixes and then fall back to isOAuthJwt is a clean way to treat bare RFC 9068 access tokens as machine tokens while preserving existing prefix behavior. JSDoc is accurate and matches the implementation.


84-84: Mapping OAuth JWTs to TokenType.OAuthToken is consistent with isMachineToken

Including isOAuthJwt(token) in the OAuth branch of getMachineTokenType keeps type resolution aligned with isMachineToken, so prefix‑less OAuth JWTs are consistently classified as oauth_token. This matches the PR objective and looks correct.

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

🧹 Nitpick comments (1)
packages/backend/src/tokens/machine.ts (1)

1-1: Consider tightening typing of OAUTH_ACCESS_TOKEN_TYPES.

Reusing decodeJwt with a guarded try/catch and validating the header typ against RFC 9068 values is a solid approach for isOAuthJwt. To make (typeof OAUTH_ACCESS_TOKEN_TYPES)[number] a useful string-literal union and better match the guidelines about as const for literals, you can mark the array as a const tuple:

-export const OAUTH_ACCESS_TOKEN_TYPES = ['at+jwt', 'application/at+jwt'];
+export const OAUTH_ACCESS_TOKEN_TYPES = ['at+jwt', 'application/at+jwt'] as const;

This keeps runtime behavior the same while giving you the 'at+jwt' | 'application/at+jwt' union type for stronger typing and editor hints.

Also applies to: 18-46

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 9ceae69 and c2eb4a5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • packages/backend/src/tokens/machine.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/tokens/machine.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/tokens/machine.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/tokens/machine.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/tokens/machine.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/tokens/machine.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/tokens/machine.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/tokens/machine.ts
🧬 Code graph analysis (1)
packages/backend/src/tokens/machine.ts (1)
packages/backend/src/jwt/verifyJwt.ts (1)
  • decodeJwt (45-95)
⏰ 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). (5)
  • GitHub Check: Build Packages
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
packages/backend/src/tokens/machine.ts (3)

12-16: JWT format helper is concise and correct.

The regex cleanly matches three base64url-like segments separated by dots, and isJwtFormat keeps this structural check isolated from decoding logic. No changes needed.


62-70: Machine token detection correctly incorporates OAuth JWTs.

Combining isMachineTokenByPrefix with isOAuthJwt cleanly extends isMachineToken to support JWT-formatted OAuth access tokens without changing existing prefix-based behavior.


88-90: getMachineTokenType classification for OAuth JWTs is consistent.

Classifying tokens with the oat_ prefix or those recognized by isOAuthJwt as TokenType.OAuthToken keeps this helper in sync with isMachineToken and the new oauth_token semantics.

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

🧹 Nitpick comments (2)
packages/backend/src/tokens/__tests__/machine.test.ts (2)

99-113: New getMachineTokenType tests correctly exercise OAuth JWT routing

Verifying that all three OAuth JWT variants (two fixtures plus a createJwt-generated token) return "oauth_token" solidly covers the new branch; you could optionally add a test that a malformed three-part JWT-like string still throws to pin down that error behavior, but it’s not required.


177-205: isOAuthJwt tests validate typ-based OAuth access token detection

Both allowed typ values plus a non-OAuth typ and a malformed token are covered; you might optionally add a case where the header omits typ entirely to ensure it continues to be treated as non-OAuth, but the current coverage is already solid.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 c2eb4a5 and c427316.

📒 Files selected for processing (1)
  • packages/backend/src/tokens/__tests__/machine.test.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

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

**/*.{test,spec}.{ts,tsx,js,jsx}: Unit tests are required for all new functionality
Verify proper error handling and edge cases
Include tests for all new features

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
**/*.{test,spec,e2e}.{ts,tsx,js,jsx}

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

Use real Clerk instances for integration tests

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/tokens/__tests__/machine.test.ts
🧬 Code graph analysis (1)
packages/backend/src/tokens/__tests__/machine.test.ts (3)
packages/backend/src/tokens/machine.ts (7)
  • isMachineToken (68-70)
  • M2M_TOKEN_PREFIX (6-6)
  • OAUTH_TOKEN_PREFIX (7-7)
  • API_KEY_PREFIX (8-8)
  • getMachineTokenType (83-97)
  • isJwtFormat (14-16)
  • isOAuthJwt (32-46)
packages/backend/src/fixtures/machine.ts (2)
  • mockSignedOAuthAccessTokenJwt (73-74)
  • mockSignedOAuthAccessTokenJwtApplicationTyp (80-81)
packages/backend/src/fixtures/index.ts (2)
  • createJwt (167-178)
  • mockOAuthAccessTokenJwtPayload (36-46)
⏰ 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). (4)
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (4)
packages/backend/src/tokens/__tests__/machine.test.ts (4)

3-16: Imports for JWT fixtures and helpers look correct

All newly added imports are used, paths match the described fixture locations, and they line up with the new token helper APIs with no redundancy.


18-40: Prefix-based machine token tests remain thorough

The suite still covers all recognized prefixes plus unknown and empty-token cases, so prefix-only behavior is clearly specified and unchanged by the new JWT handling.


42-88: Comprehensive coverage for isMachineToken across prefixes and OAuth JWTs

These tests exercise all relevant paths: known prefixes, both OAuth JWT typ variants, JWTs created via createJwt, regular/non-OAuth JWTs, unknown prefixes, and empty strings, giving good confidence in the new isOAuthJwt integration.


164-175: isJwtFormat tests clearly define the accepted JWT-like structure

Positive and negative examples around the three-segment header.payload.signature pattern look correct and match the intended “format only” semantics without over-constraining base64 details.

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

🧹 Nitpick comments (2)
packages/backend/src/tokens/verify.ts (2)

212-226: Consider removing redundant try-catch around decodeJwt.

The decodeJwt function returns a result type (JwtReturnType) rather than throwing exceptions. The error case is already handled at lines 228-240. This try-catch adds unnecessary nesting.

-  let decoded: JwtReturnType<Jwt, TokenVerificationError>;
-  try {
-    decoded = decodeJwt(accessToken);
-  } catch (e) {
-    return {
-      data: undefined,
-      tokenType: TokenType.OAuthToken,
-      errors: [
-        new MachineTokenVerificationError({
-          code: MachineTokenVerificationErrorCode.TokenInvalid,
-          message: (e as Error).message,
-        }),
-      ],
-    };
-  }
-
-  const { data: decodedResult, errors } = decoded;
+  const { data: decodedResult, errors } = decodeJwt(accessToken);

246-263: Consider extracting key resolution logic to reduce duplication.

The key resolution logic (PEM vs remote JWKS) is duplicated from verifyToken (lines 114-132). Extracting this to a shared helper would improve maintainability.

A helper could return JwtReturnType<JsonWebKey, Error>:

async function resolveJwk(
  kid: string,
  options: Pick<VerifyTokenOptions, 'jwtKey' | 'secretKey' | 'apiUrl' | 'apiVersion' | 'skipJwksCache'>
): Promise<JwtReturnType<JsonWebKey, Error>> {
  if (options.jwtKey) {
    return { data: loadClerkJwkFromPem({ kid, pem: options.jwtKey }) };
  }
  if (options.secretKey) {
    return { data: await loadClerkJWKFromRemote({ ...options, kid }) };
  }
  return { errors: [new Error('Failed to resolve JWK during verification.')] };
}

Both verifyToken and verifyJwtOAuthToken could then use this helper with appropriate error wrapping.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear 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 c427316 and 4ca5379.

📒 Files selected for processing (1)
  • packages/backend/src/tokens/verify.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

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

All code must pass ESLint checks with the project's configuration

Files:

  • packages/backend/src/tokens/verify.ts
**/*.{js,jsx,ts,tsx,json,md,yml,yaml}

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

Use Prettier for consistent code formatting

Files:

  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx}

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

TypeScript is required for all packages

Files:

  • packages/backend/src/tokens/verify.ts
**/*.{ts,tsx,js,jsx}

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

Follow established naming conventions (PascalCase for components, camelCase for variables)

Prefer importing types from @clerk/shared/types instead of the deprecated @clerk/types alias

Files:

  • packages/backend/src/tokens/verify.ts
packages/**/src/**/*.{ts,tsx,js,jsx}

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

packages/**/src/**/*.{ts,tsx,js,jsx}: Maintain comprehensive JSDoc comments for public APIs
Use tree-shaking friendly exports
Validate all inputs and sanitize outputs
All public APIs must be documented with JSDoc
Use dynamic imports for optional features
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Implement proper logging with different levels

Files:

  • packages/backend/src/tokens/verify.ts
**/*.ts?(x)

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

Use proper TypeScript error types

Files:

  • packages/backend/src/tokens/verify.ts
**/*.{ts,tsx}

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

**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Implement type guards for unknown types using the pattern function isType(value: unknown): value is Type
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details in classes
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Use mixins for shared behavior across unrelated classes in TypeScript
Use generic constraints with bounded type parameters like <T extends { id: string }>
Use utility types like Omit, Partial, and Pick for data transformation instead of manual type construction
Use discriminated unions instead of boolean flags for state management and API responses
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation at the type level
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Document functions with JSDoc comments including @param, @returns, @throws, and @example tags
Create custom error classes that extend Error for specific error types
Use the Result pattern for error handling instead of throwing exceptions
Use optional chaining (?.) and nullish coalescing (??) operators for safe property access
Let TypeScript infer obvious types to reduce verbosity
Use const assertions with as const for literal types
Use satisfies operator for type checking without widening types
Declare readonly arrays and objects for immutable data structures
Use spread operator and array spread for immutable updates instead of mutations
Use lazy loading for large types...

Files:

  • packages/backend/src/tokens/verify.ts
🧬 Code graph analysis (1)
packages/backend/src/tokens/verify.ts (4)
packages/backend/src/jwt/verifyJwt.ts (2)
  • decodeJwt (45-95)
  • verifyJwt (129-187)
packages/backend/src/tokens/tokenTypes.ts (2)
  • TokenType (1-6)
  • TokenType (11-11)
packages/backend/src/tokens/keys.ts (2)
  • loadClerkJwkFromPem (52-86)
  • loadClerkJWKFromRemote (131-173)
packages/backend/src/tokens/machine.ts (2)
  • OAUTH_ACCESS_TOKEN_TYPES (22-22)
  • isJwtFormat (14-16)
⏰ 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). (5)
  • GitHub Check: Formatting | Dedupe | Changeset
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (4)
packages/backend/src/tokens/verify.ts (4)

2-4: LGTM - Import changes are well-structured.

The imports correctly use type-only syntax for APIKey and M2MToken while keeping IdPOAuthAccessToken as a value import since its static method is invoked. The new machine module imports are all utilized in the added functionality.

Also applies to: 18-18


265-286: LGTM - JWT verification flow is correctly implemented.

The verification correctly:

  • Passes headerType: OAUTH_ACCESS_TOKEN_TYPES to enforce RFC 9068 compliance (at+jwt/application/at+jwt).
  • Maps verification errors to MachineTokenVerificationError with appropriate codes.
  • Converts the verified payload to IdPOAuthAccessToken using the static factory method.

304-306: LGTM - Clean delegation for JWT vs opaque token verification.

This correctly routes JWT-formatted OAuth tokens to local cryptographic verification while preserving the BAPI fallback for opaque tokens (e.g., oat_ prefixed). Enables networkless verification as intended.


340-342: Verify intent: JWT-format tokens default to OAuth verification path.

The condition isJwtFormat(token) routes any JWT-formatted token without a known prefix to OAuth verification. This relies on assertHeaderType (via verifyJwt) to reject non-OAuth JWTs that don't have typ: 'at+jwt' or application/at+jwt headers.

This appears intentional since:

  • Function is specifically for machine tokens, not session tokens.
  • Header type assertion provides the safety net for misrouted tokens.

Confirm this is the expected behavior for the use case. If a session JWT is accidentally passed, it will fail with a header type mismatch rather than an "unknown token type" error.

@wobsoriano wobsoriano merged commit 6dbb02b into main Dec 5, 2025
107 of 112 checks passed
@wobsoriano wobsoriano deleted the rob/user-4076-oat branch December 5, 2025 17:46
wobsoriano added a commit that referenced this pull request Dec 8, 2025
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Jacob Foshee <jacob.foshee@clerk.dev>
wobsoriano added a commit that referenced this pull request Dec 8, 2025
…7394)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Jacob Foshee <jacob.foshee@clerk.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants