Skip to content

fix(backend): validate JWT issuer (iss) claim when an issuer option is provided#8772

Open
jacekradko wants to merge 2 commits into
mainfrom
jacek/backend-issuer-validation
Open

fix(backend): validate JWT issuer (iss) claim when an issuer option is provided#8772
jacekradko wants to merge 2 commits into
mainfrom
jacek/backend-issuer-validation

Conversation

@jacekradko

@jacekradko jacekradko commented Jun 8, 2026

Copy link
Copy Markdown
Member

verifyJwt never actually validated the iss claim. VerifyJwtOptions had no issuer field, the IssuerResolver type in jwt/assertions.ts was unused, and eleven tests in verifyJwt.test.ts passed an issuer: that nothing read, so they passed regardless. This wires it up.

assertIssuerClaim is the actual change, and it skips when no issuer is configured:

if (!issuer) return; // no issuer -> skip, same as audience/azp

Nobody passes issuer today (including the networkless jwtKey path and the machine/OAuth verifiers that share verifyJwt), so current behavior doesn't change, and verifyToken/authenticateRequest pick the option up through the existing options composition. The eleven inert tests now exercise the assertion, and I added the negatives that were missing: a mismatched string, a rejecting predicate, and a verifyToken({ issuer }) case to confirm the option reaches the public API. This does not turn issuer validation on by default in authenticateRequest; that's a separate change.

Summary by CodeRabbit

  • New Features

    • Token verification now supports an optional issuer constraint: provide a string for exact-match or a function for custom validation; omitting it skips issuer checks.
  • Bug Fixes

    • Issuer validation is now properly enforced when configured and reports a clear invalid-issuer error on mismatch.
  • Documentation

    • Added notes documenting the issuer validation behavior and opt-in nature.

@changeset-bot

changeset-bot Bot commented Jun 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 73cdabf

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 8, 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 11, 2026 3:39am
swingset Ready Ready Preview, Comment Jun 11, 2026 3:39am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: abdbdaef-f82f-440d-a2cd-6ca9134649a8

📥 Commits

Reviewing files that changed from the base of the PR and between 4602902 and 73cdabf.

📒 Files selected for processing (2)
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/tokens/tests/verify.test.ts

📝 Walkthrough

Walkthrough

This PR makes issuer (string or predicate) an opt-in verifier for JWTs: verifyJwt() and verifyToken() now validate the token payload's iss claim when an optional issuer resolver is provided, and add a TokenInvalidIssuer error reason for mismatches.

Changes

JWT Issuer Validation

Layer / File(s) Summary
Error reason definition
packages/backend/src/errors.ts
TokenInvalidIssuer added to TokenVerificationErrorReason to enable issuer mismatch error reporting.
Issuer assertion and imports
packages/backend/src/jwt/assertions.ts, packages/backend/src/jwt/verifyJwt.ts (imports)
Adds assertIssuerClaim() and the IssuerResolver import/type; assertion validates JWT iss against optional issuer resolver (string or predicate) and throws TokenVerificationError with token-invalid-issuer on mismatch.
verifyJwt options and wiring
packages/backend/src/jwt/verifyJwt.ts
VerifyJwtOptions extended with optional issuer?: IssuerResolver; verifyJwt() destructures issuer and calls assertIssuerClaim(iss, issuer) during payload verification after signature validation.
Assertion unit tests
packages/backend/src/jwt/__tests__/assertions.test.ts
Tests for assertIssuerClaim() covering optional issuer, exact-match acceptance/rejection, predicate acceptance/rejection, and missing/invalid iss errors.
Integration tests
packages/backend/src/jwt/__tests__/verifyJwt.test.ts, packages/backend/src/tokens/__tests__/verify.test.ts
Tests validating verifyJwt() behavior for issuer string mismatch, predicate rejection, omitted issuer (no validation), and verifyToken() issuer mismatch returning token-invalid-issuer.
Release notes
.changeset/backend-issuer-validation.md
Changeset describing the patch release: issuer option now affects iss claim validation (string equality or predicate), and validation is opt-in when issuer is omitted.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • wobsoriano

Poem

🐰 A rabbit checks the JWT, so spry,
Hops on the iss and gives a try,
String or predicate, hop or stay,
If mismatch found, it hops away.
Cheers for validated tokens — hi! 🥕

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: validating JWT issuer claims when an issuer option is provided, which aligns with the changeset's core functionality.
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.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@pkg-pr-new

pkg-pr-new Bot commented Jun 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 73cdabf

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-08T00:27:14.399Z

Summary

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

🤖 This report was reviewed by claude-sonnet-4-6.

Note
Break Check could not snapshot 3 subpaths; the diff below excludes them.

  • @clerk/astro ./env: Internal Error: Unable to determine module for: /home/runner/_work/javascript/javascript/packages/astro/env.d.ts You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/shared ./cookie: Internal Error: Unable to follow symbol for "Cookies" You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/testing ./cypress: Symbol not found for identifier: Cypress

@clerk/backend

Current version: 3.5.0
Recommended bump: MINOR → 3.6.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: verifyToken
// ... 1 unchanged line elided ...
      audience?: string | string[] | undefined;
      authorizedParties?: string[] | undefined;
      clockSkewInMs?: number | undefined;
+     issuer?: import("./jwt/assertions").IssuerResolver | undefined;
      headerType?: string | string[] | undefined;
      secretKey?: string | undefined;
      apiUrl?: string | undefined;
// ... 5 unchanged lines elided ...

Static analyzer: Breaking change in function verifyToken: Parameter options type changed: {audience?:string|string[]|undefined;authorizedParties?:string[]|undefined;clockSkewInMs?:number|undefined;headerType?:string|string[]|undefined;secretKey?:string|undefined;apiUrl?:string|undefined;apiVersion?:string|undefined;skipJwksCache?:boolean|undefined;jwksCacheTtlInMs?:number|undefined;jwtKey?:string|undefined;}{audience?:string|string[]|undefined;authorizedParties?:string[]|undefined;clockSkewInMs?:number|undefined;issuer?:import("@clerk/backend").~IssuerResolver|undefined;headerType?:string|string[]|undefined;secretKey?:string|undefined;apiUrl?:string|undefined;apiVersion?:string|undefined;skipJwksCache?:boolean|undefined;jwksCacheTtlInMs?:number|undefined;jwtKey?:string|undefined;}

🤖 AI review (reclassified as non-breaking) (95%): The change only adds a new optional property issuer? to the options parameter object; existing callers who do not pass issuer are unaffected, and adding an optional field to an input type is non-breaking.

Subpath ./errors

🟡 Non-breaking Changes (1)

Modified: TokenVerificationErrorReason
// ... 2 unchanged lines elided ...
      TokenInvalid: string;
      TokenInvalidAlgorithm: string;
      TokenInvalidAuthorizedParties: string;
+     TokenInvalidIssuer: string;
      TokenInvalidSignature: string;
      TokenNotActiveYet: string;
      TokenIatInTheFuture: string;
// ... 9 unchanged lines elided ...

Static analyzer: Breaking change in variable TokenVerificationErrorReason: Type changed: TokenVerificationErrorReason:{TokenExpired:string;TokenInvalid:string;TokenInvalidAlgorithm:string;TokenInvalidAuthoriz…TokenVerificationErrorReason:{TokenExpired:string;TokenInvalid:string;TokenInvalidAlgorithm:string;TokenInvalidAuthoriz…

🤖 AI review (reclassified as non-breaking) (90%): A new property TokenInvalidIssuer was added to the TokenVerificationErrorReason object; no existing properties were removed or changed, so consumers reading or switching on existing keys are unaffected. This is a purely additive change to the variable's shape.

Subpath ./jwt

🟡 Non-breaking Changes (1)

Modified: VerifyJwtOptions
// ... 1 unchanged line elided ...
      audience?: string | string[];
      authorizedParties?: string[];
      clockSkewInMs?: number;
+     issuer?: IssuerResolver;
      key: JsonWebKey | string;
      headerType?: string | string[];
  };

Static analyzer: Breaking change in type alias VerifyJwtOptions: Type changed: {audience?:string|string[];authorizedParties?:string[];clockSkewInMs?:number;key:!JsonWebKey:interface|string;headerTyp…{audience?:string|string[];authorizedParties?:string[];clockSkewInMs?:number;issuer?:import("@clerk/backend").~IssuerRe…

🤖 AI review (reclassified as non-breaking) (95%): The change only adds a new optional property issuer? to VerifyJwtOptions, which is used as a function input parameter; existing callers that do not pass issuer remain valid, and no existing property was removed or made required.


Report generated by Break Check

Last ran on 4602902. Pushes that change no tracked declarations (no API surface change vs. base) are skipped and don't update this comment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/backend/src/jwt/__tests__/assertions.test.ts`:
- Around line 237-242: Update the tests in
packages/backend/src/jwt/__tests__/assertions.test.ts to stop treating an
explicitly configured-but-empty issuer as "opt-in": change the expectation for
assertIssuerClaim(iss, '') to expect it to throw (i.e., configured-empty should
fail), and add a new predicate-edge-case test that calls assertIssuerClaim with
a non-string or missing iss (e.g., an object with iss:number or undefined)
alongside a predicate function to verify it fails in a controlled way; reference
the assertIssuerClaim function and adjust the related assertions in both the
block around the current opt-in test and the second block noted (lines ~254-267)
to cover these failure paths.

In `@packages/backend/src/jwt/assertions.ts`:
- Around line 99-115: assertIssuerClaim currently treats an empty-string issuer
as "not configured" and calls predicate resolvers with iss cast to string, which
can skip validation or throw non-TokenVerificationError exceptions; change the
guard to check for issuer === undefined (so empty string is treated as
configured) and ensure the predicate is only invoked when typeof iss ===
'string' (otherwise throw TokenVerificationError with
TokenVerificationErrorAction.EnsureClerkJWT and
TokenVerificationErrorReason.TokenInvalidIssuer); update the isValid computation
in assertIssuerClaim to perform the typeof iss === 'string' check before calling
a function issuer(iss) and produce the stable token-invalid-issuer error when
iss is missing or malformed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 217d0731-ea9e-47a4-a02c-b7fb4e77af3a

📥 Commits

Reviewing files that changed from the base of the PR and between 565a516 and 4602902.

📒 Files selected for processing (7)
  • .changeset/backend-issuer-validation.md
  • packages/backend/src/errors.ts
  • packages/backend/src/jwt/__tests__/assertions.test.ts
  • packages/backend/src/jwt/__tests__/verifyJwt.test.ts
  • packages/backend/src/jwt/assertions.ts
  • packages/backend/src/jwt/verifyJwt.ts
  • packages/backend/src/tokens/__tests__/verify.test.ts

Comment on lines +237 to +242
it('does not throw if no issuer is provided (opt-in)', () => {
expect(() => assertIssuerClaim(iss)).not.toThrow();
expect(() => assertIssuerClaim(iss, undefined)).not.toThrow();
expect(() => assertIssuerClaim(undefined)).not.toThrow();
expect(() => assertIssuerClaim(iss, '')).not.toThrow();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Adjust issuer tests to avoid codifying validation bypass and cover predicate edge-case errors.

The current case at Line 241 (issuer: '') asserts skip behavior even though issuer is supplied. Add/adjust tests so configured-but-empty issuer fails, and include a predicate case with non-string/missing iss to verify controlled failure behavior.

Suggested test updates
   it('does not throw if no issuer is provided (opt-in)', () => {
     expect(() => assertIssuerClaim(iss)).not.toThrow();
     expect(() => assertIssuerClaim(iss, undefined)).not.toThrow();
     expect(() => assertIssuerClaim(undefined)).not.toThrow();
-    expect(() => assertIssuerClaim(iss, '')).not.toThrow();
+    expect(() => assertIssuerClaim(iss, '')).toThrow(`Invalid JWT issuer claim (iss) "https://clerk.example.com".`);
   });
@@
   it('throws if iss is missing or not a string when an issuer string is required', () => {
     expect(() => assertIssuerClaim(undefined, iss)).toThrow(`Invalid JWT issuer claim (iss) undefined.`);
     expect(() => assertIssuerClaim(42, iss)).toThrow(`Invalid JWT issuer claim (iss) 42.`);
   });
+
+  it('throws if iss is missing when issuer resolver is a predicate', () => {
+    expect(() => assertIssuerClaim(undefined, i => i.startsWith('https://clerk.'))).toThrow(
+      `Invalid JWT issuer claim (iss) undefined.`,
+    );
+  });

As per coding guidelines, **/*.{test,spec}.{ts,tsx} should verify proper error handling and edge cases for new functionality.

Also applies to: 254-267

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/src/jwt/__tests__/assertions.test.ts` around lines 237 -
242, Update the tests in packages/backend/src/jwt/__tests__/assertions.test.ts
to stop treating an explicitly configured-but-empty issuer as "opt-in": change
the expectation for assertIssuerClaim(iss, '') to expect it to throw (i.e.,
configured-empty should fail), and add a new predicate-edge-case test that calls
assertIssuerClaim with a non-string or missing iss (e.g., an object with
iss:number or undefined) alongside a predicate function to verify it fails in a
controlled way; reference the assertIssuerClaim function and adjust the related
assertions in both the block around the current opt-in test and the second block
noted (lines ~254-267) to cover these failure paths.

Source: Coding guidelines

Comment on lines +99 to +115
export const assertIssuerClaim = (iss: unknown, issuer?: IssuerResolver) => {
// No issuer configured, skip validation. Preserves the default behavior, matching how
// the audience and authorized parties claims are only checked when an option is provided.
if (!issuer) {
return;
}

const isValid = typeof issuer === 'function' ? issuer(iss as string) : typeof iss === 'string' && iss === issuer;

if (!isValid) {
throw new TokenVerificationError({
action: TokenVerificationErrorAction.EnsureClerkJWT,
reason: TokenVerificationErrorReason.TokenInvalidIssuer,
message: `Invalid JWT issuer claim (iss) ${JSON.stringify(iss)}.`,
});
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail closed for configured issuer and guard predicate execution.

Line 102 currently treats issuer: '' as “not configured”, which silently skips validation. Line 106 also calls predicate resolvers with iss as string, so malformed tokens can surface non-TokenVerificationError exceptions instead of a stable token-invalid-issuer failure.

Suggested fix
-export const assertIssuerClaim = (iss: unknown, issuer?: IssuerResolver) => {
+export const assertIssuerClaim = (iss: unknown, issuer?: IssuerResolver): void => {
   // No issuer configured, skip validation. Preserves the default behavior, matching how
   // the audience and authorized parties claims are only checked when an option is provided.
-  if (!issuer) {
+  if (typeof issuer === 'undefined') {
     return;
   }

-  const isValid = typeof issuer === 'function' ? issuer(iss as string) : typeof iss === 'string' && iss === issuer;
+  const issValue = typeof iss === 'string' ? iss : undefined;
+  let isValid = false;
+
+  if (typeof issuer === 'function') {
+    try {
+      isValid = typeof issValue === 'string' && issuer(issValue);
+    } catch {
+      isValid = false;
+    }
+  } else {
+    isValid = typeof issValue === 'string' && issValue === issuer;
+  }

   if (!isValid) {
     throw new TokenVerificationError({
       action: TokenVerificationErrorAction.EnsureClerkJWT,
       reason: TokenVerificationErrorReason.TokenInvalidIssuer,
       message: `Invalid JWT issuer claim (iss) ${JSON.stringify(iss)}.`,
     });
   }
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/src/jwt/assertions.ts` around lines 99 - 115,
assertIssuerClaim currently treats an empty-string issuer as "not configured"
and calls predicate resolvers with iss cast to string, which can skip validation
or throw non-TokenVerificationError exceptions; change the guard to check for
issuer === undefined (so empty string is treated as configured) and ensure the
predicate is only invoked when typeof iss === 'string' (otherwise throw
TokenVerificationError with TokenVerificationErrorAction.EnsureClerkJWT and
TokenVerificationErrorReason.TokenInvalidIssuer); update the isValid computation
in assertIssuerClaim to perform the typeof iss === 'string' check before calling
a function issuer(iss) and produce the stable token-invalid-issuer error when
iss is missing or malformed.

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-11T03:41:26.094Z

Summary

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

Warning
1 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

Note
Break Check could not snapshot 3 subpaths; the diff below excludes them.

  • @clerk/astro ./env: Internal Error: Unable to determine module for: /home/runner/_work/javascript/javascript/packages/astro/env.d.ts You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/shared ./cookie: Internal Error: Unable to follow symbol for "Cookies" You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/testing ./cypress: Symbol not found for identifier: Cypress

🔴 Breaking changes index (1)

Every breaking change, up front. Full diffs are in the package sections below.

Package Subpath Change
@clerk/backend . verifyToken

@clerk/backend

Current version: 3.6.1
Recommended bump: MAJOR → 4.0.0

Subpath .

🔴 Breaking Changes (1)

Changed: verifyToken
// ... 1 unchanged line elided ...
      audience?: string | string[] | undefined;
      authorizedParties?: string[] | undefined;
      clockSkewInMs?: number | undefined;
+     issuer?: import("./jwt/assertions").IssuerResolver | undefined;
      headerType?: string | string[] | undefined;
      secretKey?: string | undefined;
      apiUrl?: string | undefined;
// ... 5 unchanged lines elided ...

Static analyzer: Breaking change in function verifyToken: Parameter options type changed: {audience?:string|string[]|undefined;authorizedParties?:string[]|undefined;clockSkewInMs?:number|undefined;headerType?:string|string[]|undefined;secretKey?:string|undefined;apiUrl?:string|undefined;apiVersion?:string|undefined;skipJwksCache?:boolean|undefined;jwksCacheTtlInMs?:number|undefined;jwtKey?:string|undefined;}{audience?:string|string[]|undefined;authorizedParties?:string[]|undefined;clockSkewInMs?:number|undefined;issuer?:import("@clerk/backend").~IssuerResolver|undefined;headerType?:string|string[]|undefined;secretKey?:string|undefined;apiUrl?:string|undefined;apiVersion?:string|undefined;skipJwksCache?:boolean|undefined;jwksCacheTtlInMs?:number|undefined;jwtKey?:string|undefined;}

🤖 AI review (confirmed) (90%): The issuer property is added as optional to the options parameter type, but it references import("./jwt/assertions").IssuerResolver — a non-public internal chunk path (./jwt/assertions) that is not a resolvable public entry point of @clerk/backend. Per rule 12, this makes the type non-resolvable for consumers, degrading to any or a compile error. Additionally, the new IssuerResolver type is imported from a private internal path rather than the public @clerk/backend export, so consumers who do pass an issuer value cannot correctly type it.

Migration: Avoid passing the issuer option until the IssuerResolver type is exported from a public @clerk/backend entry point; if you need to use it, cast to any or wait for the public export.

Subpath ./errors

🟡 Non-breaking Changes (1)

Modified: TokenVerificationErrorReason
// ... 2 unchanged lines elided ...
      TokenInvalid: string;
      TokenInvalidAlgorithm: string;
      TokenInvalidAuthorizedParties: string;
+     TokenInvalidIssuer: string;
      TokenInvalidSignature: string;
      TokenNotActiveYet: string;
      TokenIatInTheFuture: string;
// ... 9 unchanged lines elided ...

Static analyzer: Breaking change in variable TokenVerificationErrorReason: Type changed: {TokenExpired:string;TokenInvalid:string;TokenInvalidAlgorithm:string;TokenInvalidAuthorizedParties:string;TokenInvalid…{TokenExpired:string;TokenInvalid:string;TokenInvalidAlgorithm:string;TokenInvalidAuthorizedParties:string;TokenInvalid…

🤖 AI review (reclassified as non-breaking) (90%): The only structural difference between before and after is the addition of a new TokenInvalidIssuer property to the TokenVerificationErrorReason object; no existing properties were removed or changed, making this purely additive.

Subpath ./jwt

🟡 Non-breaking Changes (1)

Modified: VerifyJwtOptions
// ... 1 unchanged line elided ...
      audience?: string | string[];
      authorizedParties?: string[];
      clockSkewInMs?: number;
+     issuer?: IssuerResolver;
      key: JsonWebKey | string;
      headerType?: string | string[];
  };

Static analyzer: Breaking change in type alias VerifyJwtOptions: Type changed: {audience?:string|string[];authorizedParties?:string[];clockSkewInMs?:number;key:!JsonWebKey:interface|string;headerTyp…{audience?:string|string[];authorizedParties?:string[];clockSkewInMs?:number;issuer?:import("@clerk/backend").~IssuerRe…

🤖 AI review (reclassified as non-breaking) (95%): The change adds a new optional property issuer? to VerifyJwtOptions, which is an input type (used as a parameter to verifyJwt). Adding an optional property to an input type is non-breaking because existing callers do not need to supply it.


Report generated by Break Check

Last ran on 73cdabf.

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.

1 participant