You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This fixes JWT audience validation for tokens whose aud claim is an array.
verifyJwt now passes the raw aud claim and configured audience option into assertAudienceClaim. The assertion helper already normalizes string and string-array inputs, so this lets it evaluate array audiences correctly.
Scope
This does not mean default Clerk session tokens have an aud claim. They generally do not, and this PR does not change the existing behavior for tokens with no aud: if the token has no audience claim, verifyJwt still skips audience validation.
The bug only applies when both of these are true:
the caller configured audience
the token payload contains aud as a string array
That shape is valid JWT syntax and is already represented in this package's M2M token parsing fallback (aud?: string[]). Clerk-issued M2M tokens also allow custom claims, and aud is not reserved there. So the fix is scoped to correctly enforcing the existing audience option when an array aud is present, not to introducing audience binding for all Clerk tokens.
Root Cause
verifyJwt wrapped both values before calling the assertion:
assertAudienceClaim([aud],[audience]);
When the token already had aud as a string array, the assertion received a nested array. That shape skipped both validation branches: it was not a string, and it was not an array of strings. The function returned without enforcing the configured audience.
Tests
Added assertion coverage for two string arrays with no intersection.
Added verifier coverage using signed JWTs:
accepts an array aud when it includes the configured audience
rejects an array aud when it does not include the configured audience
🚧 Files skipped from review as they are similar to previous changes (1)
packages/backend/src/jwt/tests/verifyJwt.test.ts
📝 Walkthrough
Walkthrough
verifyJwt now calls assertAudienceClaim(aud, audience) without wrapping values in arrays. Tests added: a unit test for assertAudienceClaim that expects an "Invalid JWT audience claim array" error when arrays do not intersect, and two verifyJwt tests that sign tokens with aud as an array — one verifies success when the configured audience is present, the other verifies failure when it is absent.
Estimated code review effort
🎯 2 (Simple) | ⏱️ ~12 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name
Status
Explanation
Resolution
Docstring Coverage
⚠️ Warning
Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.
Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name
Status
Explanation
Title check
✅ Passed
The title 'fix(backend): Fix JWT array audience validation' accurately and concisely describes the main change: fixing JWT audience validation when the aud claim is an array.
Description check
✅ Passed
The description comprehensively explains the fix, root cause, scope, tests added, and validation steps performed, all directly related to the JWT array audience validation changes.
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.
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
Comment @coderabbitai help to get the list of available commands and usage tips.
Also I understand skipping verification if we have no expected audience, but it seems odd to skip it if we do expect something and the token doesn't provide any.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This fixes JWT audience validation for tokens whose
audclaim is an array.verifyJwtnow passes the rawaudclaim and configuredaudienceoption intoassertAudienceClaim. The assertion helper already normalizes string and string-array inputs, so this lets it evaluate array audiences correctly.Scope
This does not mean default Clerk session tokens have an
audclaim. They generally do not, and this PR does not change the existing behavior for tokens with noaud: if the token has no audience claim,verifyJwtstill skips audience validation.The bug only applies when both of these are true:
audienceaudas a string arrayThat shape is valid JWT syntax and is already represented in this package's M2M token parsing fallback (
aud?: string[]). Clerk-issued M2M tokens also allow custom claims, andaudis not reserved there. So the fix is scoped to correctly enforcing the existingaudienceoption when an arrayaudis present, not to introducing audience binding for all Clerk tokens.Root Cause
verifyJwtwrapped both values before calling the assertion:When the token already had
audas a string array, the assertion received a nested array. That shape skipped both validation branches: it was not a string, and it was not an array of strings. The function returned without enforcing the configured audience.Tests
audwhen it includes the configured audienceaudwhen it does not include the configured audienceValidation
NODE_OPTIONS=--no-experimental-webstorage pnpm --filter @clerk/backend buildpnpm --filter @clerk/backend build:runtimeNODE_OPTIONS=--no-experimental-webstorage pnpm exec vitest run src/jwt/__tests__/assertions.test.ts src/jwt/__tests__/verifyJwt.test.ts --environment node --typecheck.enabled=falsepnpm --filter @clerk/backend format:checkgit diff --check