Skip to content

feat(backend): Validate cat header when verifying M2M JWTs#9038

Merged
wobsoriano merged 2 commits into
mainfrom
rob/user-5437-require-m2m-cat-header-for-sdk-verification-of-m2m-jwt-after
Jun 29, 2026
Merged

feat(backend): Validate cat header when verifying M2M JWTs#9038
wobsoriano merged 2 commits into
mainfrom
rob/user-5437-require-m2m-cat-header-for-sdk-verification-of-m2m-jwt-after

Conversation

@wobsoriano

@wobsoriano wobsoriano commented Jun 29, 2026

Copy link
Copy Markdown
Member

Description

The backend SDK verifies M2M JWTs networklessly, so it never hits the bapi-proxy /m2m_tokens/verify gate added in clerk/cloudflare-workers#2013. This adds the same cat (token-category) check to verifyM2MJwt.

M2M JWTs now carry cat: cl_B7d4PD333AAA (JWT_CATEGORY_M2M_TOKEN) in their protected header. The check rejects a JWT whose cat belongs to another class (session, jwt-template) signed by the same instance key, closing a masquerade where a non-M2M JWT with a sub starting mch_ would verify as M2M.

This is non-breaking. Absent cat is still accepted because tokens minted before the workers rollout (2026-05-27) have none, and M2M tokens can be non-expiring.

Resolves USER-5437

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

  • Bug Fixes
    • Strengthened M2M JWT verification by checking the token category header and rejecting tokens that appear to be for a different JWT class.
    • Continued support for existing M2M tokens that don’t include the category header during rollout.
    • Added coverage to ensure valid M2M tokens pass and invalid category values are rejected.

@changeset-bot

changeset-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1b98fad

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

This PR includes changesets to release 10 packages
Name Type
@clerk/backend Patch
@clerk/astro Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/hono Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router 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

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jun 29, 2026 10:36pm
swingset Ready Ready Preview, Comment Jun 29, 2026 10:36pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a cat (token-category) header check to verifyM2MJwt. A new exported constant JWT_CATEGORY_M2M_TOKEN defines the expected value; tokens with a mismatched cat are rejected early with TokenInvalid. Tokens without cat continue to pass. JwtHeader gains an optional cat field.

Changes

M2M JWT category header verification

Layer / File(s) Summary
JWT_CATEGORY_M2M_TOKEN constant and JwtHeader.cat type
packages/backend/src/tokens/machine.ts, packages/shared/src/types/jwtv2.ts
Exports JWT_CATEGORY_M2M_TOKEN constant and adds optional cat?: string field to JwtHeader.
cat header guard in verifyM2MJwt
packages/backend/src/jwt/verifyMachineJwt.ts
Imports JWT_CATEGORY_M2M_TOKEN and adds an early guard rejecting tokens whose cat is present but not equal to the M2M category value, returning TokenInvalid.
Tests and changeset
packages/backend/src/tokens/__tests__/verify.test.ts, .changeset/m2m-cat-header-verification.md
Updates createSignedM2MJwt helper to accept an optional cat argument, adds tests for no-cat acceptance and non-M2M cat rejection, and includes a changeset entry.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 A header called cat now guards the JWT gate,
If it's not M2M, we reject it straight!
No JWKS needed, we short-circuit the chase,
The rollout window keeps undefined in its place.
Hop along, valid tokens — you're safe in this space! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: backend validation of the M2M JWT cat header.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-29T22:37:11.841Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 0
🔴 Breaking changes 0
🟡 Non-breaking changes 0
🟢 Additions 0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on 1b98fad.

@wobsoriano wobsoriano force-pushed the rob/user-5437-require-m2m-cat-header-for-sdk-verification-of-m2m-jwt-after branch from 78a8ca2 to c00c34f Compare June 29, 2026 22:28
@pkg-pr-new

pkg-pr-new Bot commented Jun 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9038

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9038

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9038

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9038

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9038

@clerk/upgrade

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

@clerk/vue

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

commit: 1b98fad

@wobsoriano wobsoriano merged commit 07e1b06 into main Jun 29, 2026
51 checks passed
@wobsoriano wobsoriano deleted the rob/user-5437-require-m2m-cat-header-for-sdk-verification-of-m2m-jwt-after branch June 29, 2026 22:43
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.

2 participants