Skip to content

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option - #8560

Open
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Open

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option#8560
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalanjescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • create server-side auth clients from resolved runtime middleware options before calling authenticateRequest
  • cover Fastify, Express, Astro, Nuxt, React Router, and TanStack Start so nonce handshake payload exchange can use keys passed directly to framework middleware
  • add focused regression coverage for runtime key propagation into client construction

Context

Forced handshake nonce transport stores a short __clerk_handshake_nonce instead of the large __clerk_handshake payload. Server SDKs then need to exchange that nonce through the Backend API client attached to authenticateRequest.

Several framework wrappers passed runtime secretKey/publishableKey into authenticateRequest, but constructed the request client from environment defaults or earlier unresolved options. That means apps loading keys asynchronously and passing them into middleware could authenticate some paths with the runtime key while nonce payload exchange still used a client created without that key.

Hono and Next.js already build the client from runtime options, so no patch was needed there.

Performance

This patch avoids adding a new per-request client construction path in the common static-key cases:

  • Fastify creates the Clerk client once when clerkPlugin() registers middleware.
  • Express creates the default client once when clerkMiddleware() is created for static options. The callback form can still create a middleware/client per request, but that was already how callback options worked.
  • Astro, Nuxt, React Router, and TanStack Start already created the server Clerk client inside the request path before this PR. This patch passes the resolved options into that existing construction instead of introducing another client creation.
  • createClerkClient() itself does not perform network I/O; it builds the Backend API resource client, authenticateRequest closure, and telemetry collector. The nonce exchange network call only happens when authenticateRequest reaches forced handshake nonce handling.

Testing

  • pnpm -C packages/fastify exec vitest run src/__tests__/withClerkMiddleware.test.ts
  • pnpm -C packages/express exec vitest run src/__tests__/clerkMiddleware.test.ts -t "builds a per-middleware ClerkClient with runtime keys"
  • pnpm -C packages/react-router exec vitest run src/server/__tests__/clerkMiddleware.test.ts
  • pnpm -C packages/nuxt exec vitest run src/runtime/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/astro exec vitest run src/server/__tests__/clerk-client.test.ts
  • pnpm -C packages/tanstack-react-start exec vitest run src/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/fastify build
  • pnpm -C packages/express build
  • pnpm -C packages/fastify lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/express lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/astro lint (passes with existing warnings)
  • git diff --check

Notes

  • Full Express middleware tests currently hit the local backend/shared export mismatch: getAutoProxyUrlFromEnvironment is not a function.
  • React Router, Nuxt, Astro, and TanStack builds/lints still hit existing local package-linkage/DTS issues unrelated to this patch (missing local @clerk/react/@clerk/vue exports or declarations). Their JS builds reached the compile stage before those existing declaration/linkage failures.

@vercel

vercelBot commented May 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 2, 2026 12:47am

Request Review

@changeset-bot

changeset-botBot commented May 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e5bb059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
NameType
@clerk/fastifyMinor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jescalanjescalan changed the title [codex] fix(fastify): use runtime keys for auth clientfix(fastify): use runtime keys for auth clientMay 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@jeremy-clerkjeremy-clerk 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.

LGTM

@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR initializes a single Clerk client at middleware setup using resolved runtime keys, adds an optional enableHandshake flag to control handshake/redirect handling, strips handshake cookies/query params when disabled, and attaches the Clerk client to requests as request.clerk. Type declarations and the plugin decoration are updated accordingly, and tests were added to verify runtime key usage, enableHandshake behavior, handshake cookie stripping, and request.clerk exposure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe title accurately describes the main changes: implementing runtime key usage for auth client initialization and adding an explicit handshake opt-out option to Fastify.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the problem context, solution, performance implications, testing, and notes about related packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-newBot commented May 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e5bb059

@jescalan
jescalanforce-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99CompareMay 15, 2026 15:58
@jescalanjescalan changed the title fix(fastify): use runtime keys for auth client[codex] fix framework runtime keys for nonce handshakesMay 15, 2026
@jescalan
jescalan marked this pull request as draft May 15, 2026 15:59
@wobsoriano

Copy link
Copy Markdown
Member

@copilot can you add changes from #8563 to this PR?

CopilotAI commented May 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you add changes from #8563 to this PR?

Done — I pulled in the changes from #8563 in commits 19f3473, 0cc1e3c, and 60312d1, and added a follow-up fix for the React Router CI type error in 9243a0a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /usr/local/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@wobsoriano
wobsoriano marked this pull request as ready for review May 16, 2026 00:14
@jorgevrgs

Copy link
Copy Markdown

It looks like enableHandshake: false doesn't prevent the API to request GET /v1/client/handshake always if the cookies __clerk_handshake and/or __clerk_handshake_nonce exist. That causes a noisy log:

ERROR Clerk: HandshakeService: error getting handshake payload: ClerkAPIResponseError: Not Found
at ClientAPI.request (.../@clerk/backend/src/api/request.ts:278:21)
at async HandshakeService.getCookiesFromHandshake (.../@clerk/backend/src/tokens/handshake.ts:187:34)
at async HandshakeService.resolveHandshake (.../@clerk/backend/src/tokens/handshake.ts:220:26)
at async authenticateRequestWithTokenInCookie (.../@clerk/backend/src/tokens/request.ts:457:16)
at async Object.<anonymous> (.../@clerk/fastify/src/withClerkMiddleware.ts:88:26)

Quick solution: strip/ignore cookies/queries when the config is enableHandshake: false.

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@wobsoriano

Copy link
Copy Markdown
Member

@jorgevrgs thanks, generated another snapshot for you

npm i @clerk/fastify@3.1.28-snapshot.v20260516134813 --save-exact

@wobsorianowobsoriano changed the title chore(fastify): Use runtime keys for auth client and add enableHandshake optionchore(fastify): Use runtime keys for auth client and explicit opt-out handshake optionMay 16, 2026
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Where are we at with this one? Don't want it to linger as a single customer hotfix

…ecret-handshake
# Conflicts:
#	.changeset/silent-fastify-handshakes.md
#	packages/fastify/src/__tests__/withClerkMiddleware.test.ts
#	packages/fastify/src/clerkPlugin.ts
#	packages/fastify/src/withClerkMiddleware.ts
@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-02T00:50:48.809Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes2
🟢 Additions0

🤖 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/fastify

Current version: 3.1.32
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change to ClerkFastifyOptions is the addition of an optional property enableHandshake?: boolean. Adding an optional field to an intersection/object type used as a plugin options input is non-breaking: existing consumers who don't pass enableHandshake continue to compile and run correctly, and no previously valid consumer code becomes invalid.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change is the addition of a new optional field enableHandshake?: boolean to ClerkFastifyOptions. Since it is optional, existing consumer code that constructs or uses this type without the field continues to compile and run correctly. Adding an optional property to an intersection type is non-breaking for callers.


Report generated by Break Check

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @wobsoriano - the snapshot version command generated the following package versions:

PackageVersion
@clerk/astro3.3.3-snapshot.v20260602010447
@clerk/backend3.5.0-snapshot.v20260602010447
@clerk/chrome-extension3.1.32-snapshot.v20260602010447
@clerk/clerk-js6.13.1-snapshot.v20260602010447
@clerk/expo3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys1.1.1-snapshot.v20260602010447
@clerk/express2.1.23-snapshot.v20260602010447
@clerk/fastify3.2.0-snapshot.v20260602010447
@clerk/hono0.1.33-snapshot.v20260602010447
@clerk/localizations4.7.1-snapshot.v20260602010447
@clerk/msw0.0.31-snapshot.v20260602010447
@clerk/nextjs7.4.3-snapshot.v20260602010447
@clerk/nuxt2.5.3-snapshot.v20260602010447
@clerk/react6.7.3-snapshot.v20260602010447
@clerk/react-router3.3.3-snapshot.v20260602010447
@clerk/shared4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start1.3.3-snapshot.v20260602010447
@clerk/testing2.0.35-snapshot.v20260602010447
@clerk/ui1.14.1-snapshot.v20260602010447
@clerk/upgrade2.0.3-snapshot.v20260602010447
@clerk/vue2.3.3-snapshot.v20260602010447

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/backend

npm i @clerk/backend@3.5.0-snapshot.v20260602010447 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.32-snapshot.v20260602010447 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.13.1-snapshot.v20260602010447 --save-exact

@clerk/expo

npm i @clerk/expo@3.3.1-snapshot.v20260602010447 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.1-snapshot.v20260602010447 --save-exact

@clerk/express

npm i @clerk/express@2.1.23-snapshot.v20260602010447 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.2.0-snapshot.v20260602010447 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.33-snapshot.v20260602010447 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.7.1-snapshot.v20260602010447 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.31-snapshot.v20260602010447 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.4.3-snapshot.v20260602010447 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.5.3-snapshot.v20260602010447 --save-exact

@clerk/react

npm i @clerk/react@6.7.3-snapshot.v20260602010447 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/shared

npm i @clerk/shared@4.14.1-snapshot.v20260602010447 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.3.3-snapshot.v20260602010447 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.35-snapshot.v20260602010447 --save-exact

@clerk/ui

npm i @clerk/ui@1.14.1-snapshot.v20260602010447 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260602010447 --save-exact

@clerk/vue

npm i @clerk/vue@2.3.3-snapshot.v20260602010447 --save-exact

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Bumping this again

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Another bump here - this is debt that I feel that we need to clear out 😬

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jescalan@wobsoriano@jorgevrgs@jeremy-clerk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option by jescalan · Pull Request #8560 · clerk/javascript · GitHub
Skip to content

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option - #8560

Open
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Open

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option#8560
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalanjescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • create server-side auth clients from resolved runtime middleware options before calling authenticateRequest
  • cover Fastify, Express, Astro, Nuxt, React Router, and TanStack Start so nonce handshake payload exchange can use keys passed directly to framework middleware
  • add focused regression coverage for runtime key propagation into client construction

Context

Forced handshake nonce transport stores a short __clerk_handshake_nonce instead of the large __clerk_handshake payload. Server SDKs then need to exchange that nonce through the Backend API client attached to authenticateRequest.

Several framework wrappers passed runtime secretKey/publishableKey into authenticateRequest, but constructed the request client from environment defaults or earlier unresolved options. That means apps loading keys asynchronously and passing them into middleware could authenticate some paths with the runtime key while nonce payload exchange still used a client created without that key.

Hono and Next.js already build the client from runtime options, so no patch was needed there.

Performance

This patch avoids adding a new per-request client construction path in the common static-key cases:

  • Fastify creates the Clerk client once when clerkPlugin() registers middleware.
  • Express creates the default client once when clerkMiddleware() is created for static options. The callback form can still create a middleware/client per request, but that was already how callback options worked.
  • Astro, Nuxt, React Router, and TanStack Start already created the server Clerk client inside the request path before this PR. This patch passes the resolved options into that existing construction instead of introducing another client creation.
  • createClerkClient() itself does not perform network I/O; it builds the Backend API resource client, authenticateRequest closure, and telemetry collector. The nonce exchange network call only happens when authenticateRequest reaches forced handshake nonce handling.

Testing

  • pnpm -C packages/fastify exec vitest run src/__tests__/withClerkMiddleware.test.ts
  • pnpm -C packages/express exec vitest run src/__tests__/clerkMiddleware.test.ts -t "builds a per-middleware ClerkClient with runtime keys"
  • pnpm -C packages/react-router exec vitest run src/server/__tests__/clerkMiddleware.test.ts
  • pnpm -C packages/nuxt exec vitest run src/runtime/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/astro exec vitest run src/server/__tests__/clerk-client.test.ts
  • pnpm -C packages/tanstack-react-start exec vitest run src/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/fastify build
  • pnpm -C packages/express build
  • pnpm -C packages/fastify lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/express lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/astro lint (passes with existing warnings)
  • git diff --check

Notes

  • Full Express middleware tests currently hit the local backend/shared export mismatch: getAutoProxyUrlFromEnvironment is not a function.
  • React Router, Nuxt, Astro, and TanStack builds/lints still hit existing local package-linkage/DTS issues unrelated to this patch (missing local @clerk/react/@clerk/vue exports or declarations). Their JS builds reached the compile stage before those existing declaration/linkage failures.

@vercel

vercelBot commented May 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 2, 2026 12:47am

Request Review

@changeset-bot

changeset-botBot commented May 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e5bb059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
NameType
@clerk/fastifyMinor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jescalanjescalan changed the title [codex] fix(fastify): use runtime keys for auth clientfix(fastify): use runtime keys for auth clientMay 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@jeremy-clerkjeremy-clerk 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.

LGTM

@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR initializes a single Clerk client at middleware setup using resolved runtime keys, adds an optional enableHandshake flag to control handshake/redirect handling, strips handshake cookies/query params when disabled, and attaches the Clerk client to requests as request.clerk. Type declarations and the plugin decoration are updated accordingly, and tests were added to verify runtime key usage, enableHandshake behavior, handshake cookie stripping, and request.clerk exposure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe title accurately describes the main changes: implementing runtime key usage for auth client initialization and adding an explicit handshake opt-out option to Fastify.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the problem context, solution, performance implications, testing, and notes about related packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-newBot commented May 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e5bb059

@jescalan
jescalanforce-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99CompareMay 15, 2026 15:58
@jescalanjescalan changed the title fix(fastify): use runtime keys for auth client[codex] fix framework runtime keys for nonce handshakesMay 15, 2026
@jescalan
jescalan marked this pull request as draft May 15, 2026 15:59
@wobsoriano

Copy link
Copy Markdown
Member

@copilot can you add changes from #8563 to this PR?

CopilotAI commented May 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you add changes from #8563 to this PR?

Done — I pulled in the changes from #8563 in commits 19f3473, 0cc1e3c, and 60312d1, and added a follow-up fix for the React Router CI type error in 9243a0a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /usr/local/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@wobsoriano
wobsoriano marked this pull request as ready for review May 16, 2026 00:14
@jorgevrgs

Copy link
Copy Markdown

It looks like enableHandshake: false doesn't prevent the API to request GET /v1/client/handshake always if the cookies __clerk_handshake and/or __clerk_handshake_nonce exist. That causes a noisy log:

ERROR Clerk: HandshakeService: error getting handshake payload: ClerkAPIResponseError: Not Found
at ClientAPI.request (.../@clerk/backend/src/api/request.ts:278:21)
at async HandshakeService.getCookiesFromHandshake (.../@clerk/backend/src/tokens/handshake.ts:187:34)
at async HandshakeService.resolveHandshake (.../@clerk/backend/src/tokens/handshake.ts:220:26)
at async authenticateRequestWithTokenInCookie (.../@clerk/backend/src/tokens/request.ts:457:16)
at async Object.<anonymous> (.../@clerk/fastify/src/withClerkMiddleware.ts:88:26)

Quick solution: strip/ignore cookies/queries when the config is enableHandshake: false.

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@wobsoriano

Copy link
Copy Markdown
Member

@jorgevrgs thanks, generated another snapshot for you

npm i @clerk/fastify@3.1.28-snapshot.v20260516134813 --save-exact

@wobsorianowobsoriano changed the title chore(fastify): Use runtime keys for auth client and add enableHandshake optionchore(fastify): Use runtime keys for auth client and explicit opt-out handshake optionMay 16, 2026
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Where are we at with this one? Don't want it to linger as a single customer hotfix

…ecret-handshake
# Conflicts:
#	.changeset/silent-fastify-handshakes.md
#	packages/fastify/src/__tests__/withClerkMiddleware.test.ts
#	packages/fastify/src/clerkPlugin.ts
#	packages/fastify/src/withClerkMiddleware.ts
@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-02T00:50:48.809Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes2
🟢 Additions0

🤖 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/fastify

Current version: 3.1.32
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change to ClerkFastifyOptions is the addition of an optional property enableHandshake?: boolean. Adding an optional field to an intersection/object type used as a plugin options input is non-breaking: existing consumers who don't pass enableHandshake continue to compile and run correctly, and no previously valid consumer code becomes invalid.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change is the addition of a new optional field enableHandshake?: boolean to ClerkFastifyOptions. Since it is optional, existing consumer code that constructs or uses this type without the field continues to compile and run correctly. Adding an optional property to an intersection type is non-breaking for callers.


Report generated by Break Check

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @wobsoriano - the snapshot version command generated the following package versions:

PackageVersion
@clerk/astro3.3.3-snapshot.v20260602010447
@clerk/backend3.5.0-snapshot.v20260602010447
@clerk/chrome-extension3.1.32-snapshot.v20260602010447
@clerk/clerk-js6.13.1-snapshot.v20260602010447
@clerk/expo3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys1.1.1-snapshot.v20260602010447
@clerk/express2.1.23-snapshot.v20260602010447
@clerk/fastify3.2.0-snapshot.v20260602010447
@clerk/hono0.1.33-snapshot.v20260602010447
@clerk/localizations4.7.1-snapshot.v20260602010447
@clerk/msw0.0.31-snapshot.v20260602010447
@clerk/nextjs7.4.3-snapshot.v20260602010447
@clerk/nuxt2.5.3-snapshot.v20260602010447
@clerk/react6.7.3-snapshot.v20260602010447
@clerk/react-router3.3.3-snapshot.v20260602010447
@clerk/shared4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start1.3.3-snapshot.v20260602010447
@clerk/testing2.0.35-snapshot.v20260602010447
@clerk/ui1.14.1-snapshot.v20260602010447
@clerk/upgrade2.0.3-snapshot.v20260602010447
@clerk/vue2.3.3-snapshot.v20260602010447

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/backend

npm i @clerk/backend@3.5.0-snapshot.v20260602010447 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.32-snapshot.v20260602010447 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.13.1-snapshot.v20260602010447 --save-exact

@clerk/expo

npm i @clerk/expo@3.3.1-snapshot.v20260602010447 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.1-snapshot.v20260602010447 --save-exact

@clerk/express

npm i @clerk/express@2.1.23-snapshot.v20260602010447 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.2.0-snapshot.v20260602010447 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.33-snapshot.v20260602010447 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.7.1-snapshot.v20260602010447 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.31-snapshot.v20260602010447 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.4.3-snapshot.v20260602010447 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.5.3-snapshot.v20260602010447 --save-exact

@clerk/react

npm i @clerk/react@6.7.3-snapshot.v20260602010447 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/shared

npm i @clerk/shared@4.14.1-snapshot.v20260602010447 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.3.3-snapshot.v20260602010447 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.35-snapshot.v20260602010447 --save-exact

@clerk/ui

npm i @clerk/ui@1.14.1-snapshot.v20260602010447 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260602010447 --save-exact

@clerk/vue

npm i @clerk/vue@2.3.3-snapshot.v20260602010447 --save-exact

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Bumping this again

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Another bump here - this is debt that I feel that we need to clear out 😬

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jescalan@wobsoriano@jorgevrgs@jeremy-clerk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option by jescalan · Pull Request #8560 · clerk/javascript · GitHub
Skip to content

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option - #8560

Open
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Open

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option#8560
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalanjescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • create server-side auth clients from resolved runtime middleware options before calling authenticateRequest
  • cover Fastify, Express, Astro, Nuxt, React Router, and TanStack Start so nonce handshake payload exchange can use keys passed directly to framework middleware
  • add focused regression coverage for runtime key propagation into client construction

Context

Forced handshake nonce transport stores a short __clerk_handshake_nonce instead of the large __clerk_handshake payload. Server SDKs then need to exchange that nonce through the Backend API client attached to authenticateRequest.

Several framework wrappers passed runtime secretKey/publishableKey into authenticateRequest, but constructed the request client from environment defaults or earlier unresolved options. That means apps loading keys asynchronously and passing them into middleware could authenticate some paths with the runtime key while nonce payload exchange still used a client created without that key.

Hono and Next.js already build the client from runtime options, so no patch was needed there.

Performance

This patch avoids adding a new per-request client construction path in the common static-key cases:

  • Fastify creates the Clerk client once when clerkPlugin() registers middleware.
  • Express creates the default client once when clerkMiddleware() is created for static options. The callback form can still create a middleware/client per request, but that was already how callback options worked.
  • Astro, Nuxt, React Router, and TanStack Start already created the server Clerk client inside the request path before this PR. This patch passes the resolved options into that existing construction instead of introducing another client creation.
  • createClerkClient() itself does not perform network I/O; it builds the Backend API resource client, authenticateRequest closure, and telemetry collector. The nonce exchange network call only happens when authenticateRequest reaches forced handshake nonce handling.

Testing

  • pnpm -C packages/fastify exec vitest run src/__tests__/withClerkMiddleware.test.ts
  • pnpm -C packages/express exec vitest run src/__tests__/clerkMiddleware.test.ts -t "builds a per-middleware ClerkClient with runtime keys"
  • pnpm -C packages/react-router exec vitest run src/server/__tests__/clerkMiddleware.test.ts
  • pnpm -C packages/nuxt exec vitest run src/runtime/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/astro exec vitest run src/server/__tests__/clerk-client.test.ts
  • pnpm -C packages/tanstack-react-start exec vitest run src/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/fastify build
  • pnpm -C packages/express build
  • pnpm -C packages/fastify lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/express lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/astro lint (passes with existing warnings)
  • git diff --check

Notes

  • Full Express middleware tests currently hit the local backend/shared export mismatch: getAutoProxyUrlFromEnvironment is not a function.
  • React Router, Nuxt, Astro, and TanStack builds/lints still hit existing local package-linkage/DTS issues unrelated to this patch (missing local @clerk/react/@clerk/vue exports or declarations). Their JS builds reached the compile stage before those existing declaration/linkage failures.

@vercel

vercelBot commented May 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 2, 2026 12:47am

Request Review

@changeset-bot

changeset-botBot commented May 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e5bb059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
NameType
@clerk/fastifyMinor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jescalanjescalan changed the title [codex] fix(fastify): use runtime keys for auth clientfix(fastify): use runtime keys for auth clientMay 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@jeremy-clerkjeremy-clerk 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.

LGTM

@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR initializes a single Clerk client at middleware setup using resolved runtime keys, adds an optional enableHandshake flag to control handshake/redirect handling, strips handshake cookies/query params when disabled, and attaches the Clerk client to requests as request.clerk. Type declarations and the plugin decoration are updated accordingly, and tests were added to verify runtime key usage, enableHandshake behavior, handshake cookie stripping, and request.clerk exposure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe title accurately describes the main changes: implementing runtime key usage for auth client initialization and adding an explicit handshake opt-out option to Fastify.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the problem context, solution, performance implications, testing, and notes about related packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-newBot commented May 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e5bb059

@jescalan
jescalanforce-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99CompareMay 15, 2026 15:58
@jescalanjescalan changed the title fix(fastify): use runtime keys for auth client[codex] fix framework runtime keys for nonce handshakesMay 15, 2026
@jescalan
jescalan marked this pull request as draft May 15, 2026 15:59
@wobsoriano

Copy link
Copy Markdown
Member

@copilot can you add changes from #8563 to this PR?

CopilotAI commented May 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you add changes from #8563 to this PR?

Done — I pulled in the changes from #8563 in commits 19f3473, 0cc1e3c, and 60312d1, and added a follow-up fix for the React Router CI type error in 9243a0a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /usr/local/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@wobsoriano
wobsoriano marked this pull request as ready for review May 16, 2026 00:14
@jorgevrgs

Copy link
Copy Markdown

It looks like enableHandshake: false doesn't prevent the API to request GET /v1/client/handshake always if the cookies __clerk_handshake and/or __clerk_handshake_nonce exist. That causes a noisy log:

ERROR Clerk: HandshakeService: error getting handshake payload: ClerkAPIResponseError: Not Found
at ClientAPI.request (.../@clerk/backend/src/api/request.ts:278:21)
at async HandshakeService.getCookiesFromHandshake (.../@clerk/backend/src/tokens/handshake.ts:187:34)
at async HandshakeService.resolveHandshake (.../@clerk/backend/src/tokens/handshake.ts:220:26)
at async authenticateRequestWithTokenInCookie (.../@clerk/backend/src/tokens/request.ts:457:16)
at async Object.<anonymous> (.../@clerk/fastify/src/withClerkMiddleware.ts:88:26)

Quick solution: strip/ignore cookies/queries when the config is enableHandshake: false.

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@wobsoriano

Copy link
Copy Markdown
Member

@jorgevrgs thanks, generated another snapshot for you

npm i @clerk/fastify@3.1.28-snapshot.v20260516134813 --save-exact

@wobsorianowobsoriano changed the title chore(fastify): Use runtime keys for auth client and add enableHandshake optionchore(fastify): Use runtime keys for auth client and explicit opt-out handshake optionMay 16, 2026
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Where are we at with this one? Don't want it to linger as a single customer hotfix

…ecret-handshake
# Conflicts:
#	.changeset/silent-fastify-handshakes.md
#	packages/fastify/src/__tests__/withClerkMiddleware.test.ts
#	packages/fastify/src/clerkPlugin.ts
#	packages/fastify/src/withClerkMiddleware.ts
@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-02T00:50:48.809Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes2
🟢 Additions0

🤖 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/fastify

Current version: 3.1.32
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change to ClerkFastifyOptions is the addition of an optional property enableHandshake?: boolean. Adding an optional field to an intersection/object type used as a plugin options input is non-breaking: existing consumers who don't pass enableHandshake continue to compile and run correctly, and no previously valid consumer code becomes invalid.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change is the addition of a new optional field enableHandshake?: boolean to ClerkFastifyOptions. Since it is optional, existing consumer code that constructs or uses this type without the field continues to compile and run correctly. Adding an optional property to an intersection type is non-breaking for callers.


Report generated by Break Check

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @wobsoriano - the snapshot version command generated the following package versions:

PackageVersion
@clerk/astro3.3.3-snapshot.v20260602010447
@clerk/backend3.5.0-snapshot.v20260602010447
@clerk/chrome-extension3.1.32-snapshot.v20260602010447
@clerk/clerk-js6.13.1-snapshot.v20260602010447
@clerk/expo3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys1.1.1-snapshot.v20260602010447
@clerk/express2.1.23-snapshot.v20260602010447
@clerk/fastify3.2.0-snapshot.v20260602010447
@clerk/hono0.1.33-snapshot.v20260602010447
@clerk/localizations4.7.1-snapshot.v20260602010447
@clerk/msw0.0.31-snapshot.v20260602010447
@clerk/nextjs7.4.3-snapshot.v20260602010447
@clerk/nuxt2.5.3-snapshot.v20260602010447
@clerk/react6.7.3-snapshot.v20260602010447
@clerk/react-router3.3.3-snapshot.v20260602010447
@clerk/shared4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start1.3.3-snapshot.v20260602010447
@clerk/testing2.0.35-snapshot.v20260602010447
@clerk/ui1.14.1-snapshot.v20260602010447
@clerk/upgrade2.0.3-snapshot.v20260602010447
@clerk/vue2.3.3-snapshot.v20260602010447

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/backend

npm i @clerk/backend@3.5.0-snapshot.v20260602010447 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.32-snapshot.v20260602010447 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.13.1-snapshot.v20260602010447 --save-exact

@clerk/expo

npm i @clerk/expo@3.3.1-snapshot.v20260602010447 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.1-snapshot.v20260602010447 --save-exact

@clerk/express

npm i @clerk/express@2.1.23-snapshot.v20260602010447 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.2.0-snapshot.v20260602010447 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.33-snapshot.v20260602010447 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.7.1-snapshot.v20260602010447 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.31-snapshot.v20260602010447 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.4.3-snapshot.v20260602010447 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.5.3-snapshot.v20260602010447 --save-exact

@clerk/react

npm i @clerk/react@6.7.3-snapshot.v20260602010447 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/shared

npm i @clerk/shared@4.14.1-snapshot.v20260602010447 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.3.3-snapshot.v20260602010447 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.35-snapshot.v20260602010447 --save-exact

@clerk/ui

npm i @clerk/ui@1.14.1-snapshot.v20260602010447 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260602010447 --save-exact

@clerk/vue

npm i @clerk/vue@2.3.3-snapshot.v20260602010447 --save-exact

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Bumping this again

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Another bump here - this is debt that I feel that we need to clear out 😬

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jescalan@wobsoriano@jorgevrgs@jeremy-clerk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option by jescalan · Pull Request #8560 · clerk/javascript · GitHub
Skip to content

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option - #8560

Open
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Open

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option#8560
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalanjescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • create server-side auth clients from resolved runtime middleware options before calling authenticateRequest
  • cover Fastify, Express, Astro, Nuxt, React Router, and TanStack Start so nonce handshake payload exchange can use keys passed directly to framework middleware
  • add focused regression coverage for runtime key propagation into client construction

Context

Forced handshake nonce transport stores a short __clerk_handshake_nonce instead of the large __clerk_handshake payload. Server SDKs then need to exchange that nonce through the Backend API client attached to authenticateRequest.

Several framework wrappers passed runtime secretKey/publishableKey into authenticateRequest, but constructed the request client from environment defaults or earlier unresolved options. That means apps loading keys asynchronously and passing them into middleware could authenticate some paths with the runtime key while nonce payload exchange still used a client created without that key.

Hono and Next.js already build the client from runtime options, so no patch was needed there.

Performance

This patch avoids adding a new per-request client construction path in the common static-key cases:

  • Fastify creates the Clerk client once when clerkPlugin() registers middleware.
  • Express creates the default client once when clerkMiddleware() is created for static options. The callback form can still create a middleware/client per request, but that was already how callback options worked.
  • Astro, Nuxt, React Router, and TanStack Start already created the server Clerk client inside the request path before this PR. This patch passes the resolved options into that existing construction instead of introducing another client creation.
  • createClerkClient() itself does not perform network I/O; it builds the Backend API resource client, authenticateRequest closure, and telemetry collector. The nonce exchange network call only happens when authenticateRequest reaches forced handshake nonce handling.

Testing

  • pnpm -C packages/fastify exec vitest run src/__tests__/withClerkMiddleware.test.ts
  • pnpm -C packages/express exec vitest run src/__tests__/clerkMiddleware.test.ts -t "builds a per-middleware ClerkClient with runtime keys"
  • pnpm -C packages/react-router exec vitest run src/server/__tests__/clerkMiddleware.test.ts
  • pnpm -C packages/nuxt exec vitest run src/runtime/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/astro exec vitest run src/server/__tests__/clerk-client.test.ts
  • pnpm -C packages/tanstack-react-start exec vitest run src/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/fastify build
  • pnpm -C packages/express build
  • pnpm -C packages/fastify lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/express lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/astro lint (passes with existing warnings)
  • git diff --check

Notes

  • Full Express middleware tests currently hit the local backend/shared export mismatch: getAutoProxyUrlFromEnvironment is not a function.
  • React Router, Nuxt, Astro, and TanStack builds/lints still hit existing local package-linkage/DTS issues unrelated to this patch (missing local @clerk/react/@clerk/vue exports or declarations). Their JS builds reached the compile stage before those existing declaration/linkage failures.

@vercel

vercelBot commented May 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 2, 2026 12:47am

Request Review

@changeset-bot

changeset-botBot commented May 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e5bb059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
NameType
@clerk/fastifyMinor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jescalanjescalan changed the title [codex] fix(fastify): use runtime keys for auth clientfix(fastify): use runtime keys for auth clientMay 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@jeremy-clerkjeremy-clerk 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.

LGTM

@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR initializes a single Clerk client at middleware setup using resolved runtime keys, adds an optional enableHandshake flag to control handshake/redirect handling, strips handshake cookies/query params when disabled, and attaches the Clerk client to requests as request.clerk. Type declarations and the plugin decoration are updated accordingly, and tests were added to verify runtime key usage, enableHandshake behavior, handshake cookie stripping, and request.clerk exposure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe title accurately describes the main changes: implementing runtime key usage for auth client initialization and adding an explicit handshake opt-out option to Fastify.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the problem context, solution, performance implications, testing, and notes about related packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-newBot commented May 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e5bb059

@jescalan
jescalanforce-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99CompareMay 15, 2026 15:58
@jescalanjescalan changed the title fix(fastify): use runtime keys for auth client[codex] fix framework runtime keys for nonce handshakesMay 15, 2026
@jescalan
jescalan marked this pull request as draft May 15, 2026 15:59
@wobsoriano

Copy link
Copy Markdown
Member

@copilot can you add changes from #8563 to this PR?

CopilotAI commented May 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you add changes from #8563 to this PR?

Done — I pulled in the changes from #8563 in commits 19f3473, 0cc1e3c, and 60312d1, and added a follow-up fix for the React Router CI type error in 9243a0a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /usr/local/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@wobsoriano
wobsoriano marked this pull request as ready for review May 16, 2026 00:14
@jorgevrgs

Copy link
Copy Markdown

It looks like enableHandshake: false doesn't prevent the API to request GET /v1/client/handshake always if the cookies __clerk_handshake and/or __clerk_handshake_nonce exist. That causes a noisy log:

ERROR Clerk: HandshakeService: error getting handshake payload: ClerkAPIResponseError: Not Found
at ClientAPI.request (.../@clerk/backend/src/api/request.ts:278:21)
at async HandshakeService.getCookiesFromHandshake (.../@clerk/backend/src/tokens/handshake.ts:187:34)
at async HandshakeService.resolveHandshake (.../@clerk/backend/src/tokens/handshake.ts:220:26)
at async authenticateRequestWithTokenInCookie (.../@clerk/backend/src/tokens/request.ts:457:16)
at async Object.<anonymous> (.../@clerk/fastify/src/withClerkMiddleware.ts:88:26)

Quick solution: strip/ignore cookies/queries when the config is enableHandshake: false.

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@wobsoriano

Copy link
Copy Markdown
Member

@jorgevrgs thanks, generated another snapshot for you

npm i @clerk/fastify@3.1.28-snapshot.v20260516134813 --save-exact

@wobsorianowobsoriano changed the title chore(fastify): Use runtime keys for auth client and add enableHandshake optionchore(fastify): Use runtime keys for auth client and explicit opt-out handshake optionMay 16, 2026
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Where are we at with this one? Don't want it to linger as a single customer hotfix

…ecret-handshake
# Conflicts:
#	.changeset/silent-fastify-handshakes.md
#	packages/fastify/src/__tests__/withClerkMiddleware.test.ts
#	packages/fastify/src/clerkPlugin.ts
#	packages/fastify/src/withClerkMiddleware.ts
@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-02T00:50:48.809Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes2
🟢 Additions0

🤖 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/fastify

Current version: 3.1.32
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change to ClerkFastifyOptions is the addition of an optional property enableHandshake?: boolean. Adding an optional field to an intersection/object type used as a plugin options input is non-breaking: existing consumers who don't pass enableHandshake continue to compile and run correctly, and no previously valid consumer code becomes invalid.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change is the addition of a new optional field enableHandshake?: boolean to ClerkFastifyOptions. Since it is optional, existing consumer code that constructs or uses this type without the field continues to compile and run correctly. Adding an optional property to an intersection type is non-breaking for callers.


Report generated by Break Check

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @wobsoriano - the snapshot version command generated the following package versions:

PackageVersion
@clerk/astro3.3.3-snapshot.v20260602010447
@clerk/backend3.5.0-snapshot.v20260602010447
@clerk/chrome-extension3.1.32-snapshot.v20260602010447
@clerk/clerk-js6.13.1-snapshot.v20260602010447
@clerk/expo3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys1.1.1-snapshot.v20260602010447
@clerk/express2.1.23-snapshot.v20260602010447
@clerk/fastify3.2.0-snapshot.v20260602010447
@clerk/hono0.1.33-snapshot.v20260602010447
@clerk/localizations4.7.1-snapshot.v20260602010447
@clerk/msw0.0.31-snapshot.v20260602010447
@clerk/nextjs7.4.3-snapshot.v20260602010447
@clerk/nuxt2.5.3-snapshot.v20260602010447
@clerk/react6.7.3-snapshot.v20260602010447
@clerk/react-router3.3.3-snapshot.v20260602010447
@clerk/shared4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start1.3.3-snapshot.v20260602010447
@clerk/testing2.0.35-snapshot.v20260602010447
@clerk/ui1.14.1-snapshot.v20260602010447
@clerk/upgrade2.0.3-snapshot.v20260602010447
@clerk/vue2.3.3-snapshot.v20260602010447

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/backend

npm i @clerk/backend@3.5.0-snapshot.v20260602010447 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.32-snapshot.v20260602010447 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.13.1-snapshot.v20260602010447 --save-exact

@clerk/expo

npm i @clerk/expo@3.3.1-snapshot.v20260602010447 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.1-snapshot.v20260602010447 --save-exact

@clerk/express

npm i @clerk/express@2.1.23-snapshot.v20260602010447 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.2.0-snapshot.v20260602010447 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.33-snapshot.v20260602010447 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.7.1-snapshot.v20260602010447 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.31-snapshot.v20260602010447 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.4.3-snapshot.v20260602010447 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.5.3-snapshot.v20260602010447 --save-exact

@clerk/react

npm i @clerk/react@6.7.3-snapshot.v20260602010447 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/shared

npm i @clerk/shared@4.14.1-snapshot.v20260602010447 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.3.3-snapshot.v20260602010447 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.35-snapshot.v20260602010447 --save-exact

@clerk/ui

npm i @clerk/ui@1.14.1-snapshot.v20260602010447 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260602010447 --save-exact

@clerk/vue

npm i @clerk/vue@2.3.3-snapshot.v20260602010447 --save-exact

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Bumping this again

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Another bump here - this is debt that I feel that we need to clear out 😬

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jescalan@wobsoriano@jorgevrgs@jeremy-clerk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option by jescalan · Pull Request #8560 · clerk/javascript · GitHub
Skip to content

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option - #8560

Open
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Open

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option#8560
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalanjescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • create server-side auth clients from resolved runtime middleware options before calling authenticateRequest
  • cover Fastify, Express, Astro, Nuxt, React Router, and TanStack Start so nonce handshake payload exchange can use keys passed directly to framework middleware
  • add focused regression coverage for runtime key propagation into client construction

Context

Forced handshake nonce transport stores a short __clerk_handshake_nonce instead of the large __clerk_handshake payload. Server SDKs then need to exchange that nonce through the Backend API client attached to authenticateRequest.

Several framework wrappers passed runtime secretKey/publishableKey into authenticateRequest, but constructed the request client from environment defaults or earlier unresolved options. That means apps loading keys asynchronously and passing them into middleware could authenticate some paths with the runtime key while nonce payload exchange still used a client created without that key.

Hono and Next.js already build the client from runtime options, so no patch was needed there.

Performance

This patch avoids adding a new per-request client construction path in the common static-key cases:

  • Fastify creates the Clerk client once when clerkPlugin() registers middleware.
  • Express creates the default client once when clerkMiddleware() is created for static options. The callback form can still create a middleware/client per request, but that was already how callback options worked.
  • Astro, Nuxt, React Router, and TanStack Start already created the server Clerk client inside the request path before this PR. This patch passes the resolved options into that existing construction instead of introducing another client creation.
  • createClerkClient() itself does not perform network I/O; it builds the Backend API resource client, authenticateRequest closure, and telemetry collector. The nonce exchange network call only happens when authenticateRequest reaches forced handshake nonce handling.

Testing

  • pnpm -C packages/fastify exec vitest run src/__tests__/withClerkMiddleware.test.ts
  • pnpm -C packages/express exec vitest run src/__tests__/clerkMiddleware.test.ts -t "builds a per-middleware ClerkClient with runtime keys"
  • pnpm -C packages/react-router exec vitest run src/server/__tests__/clerkMiddleware.test.ts
  • pnpm -C packages/nuxt exec vitest run src/runtime/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/astro exec vitest run src/server/__tests__/clerk-client.test.ts
  • pnpm -C packages/tanstack-react-start exec vitest run src/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/fastify build
  • pnpm -C packages/express build
  • pnpm -C packages/fastify lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/express lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/astro lint (passes with existing warnings)
  • git diff --check

Notes

  • Full Express middleware tests currently hit the local backend/shared export mismatch: getAutoProxyUrlFromEnvironment is not a function.
  • React Router, Nuxt, Astro, and TanStack builds/lints still hit existing local package-linkage/DTS issues unrelated to this patch (missing local @clerk/react/@clerk/vue exports or declarations). Their JS builds reached the compile stage before those existing declaration/linkage failures.

@vercel

vercelBot commented May 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 2, 2026 12:47am

Request Review

@changeset-bot

changeset-botBot commented May 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e5bb059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
NameType
@clerk/fastifyMinor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jescalanjescalan changed the title [codex] fix(fastify): use runtime keys for auth clientfix(fastify): use runtime keys for auth clientMay 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@jeremy-clerkjeremy-clerk 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.

LGTM

@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR initializes a single Clerk client at middleware setup using resolved runtime keys, adds an optional enableHandshake flag to control handshake/redirect handling, strips handshake cookies/query params when disabled, and attaches the Clerk client to requests as request.clerk. Type declarations and the plugin decoration are updated accordingly, and tests were added to verify runtime key usage, enableHandshake behavior, handshake cookie stripping, and request.clerk exposure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe title accurately describes the main changes: implementing runtime key usage for auth client initialization and adding an explicit handshake opt-out option to Fastify.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the problem context, solution, performance implications, testing, and notes about related packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-newBot commented May 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e5bb059

@jescalan
jescalanforce-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99CompareMay 15, 2026 15:58
@jescalanjescalan changed the title fix(fastify): use runtime keys for auth client[codex] fix framework runtime keys for nonce handshakesMay 15, 2026
@jescalan
jescalan marked this pull request as draft May 15, 2026 15:59
@wobsoriano

Copy link
Copy Markdown
Member

@copilot can you add changes from #8563 to this PR?

CopilotAI commented May 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you add changes from #8563 to this PR?

Done — I pulled in the changes from #8563 in commits 19f3473, 0cc1e3c, and 60312d1, and added a follow-up fix for the React Router CI type error in 9243a0a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /usr/local/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@wobsoriano
wobsoriano marked this pull request as ready for review May 16, 2026 00:14
@jorgevrgs

Copy link
Copy Markdown

It looks like enableHandshake: false doesn't prevent the API to request GET /v1/client/handshake always if the cookies __clerk_handshake and/or __clerk_handshake_nonce exist. That causes a noisy log:

ERROR Clerk: HandshakeService: error getting handshake payload: ClerkAPIResponseError: Not Found
at ClientAPI.request (.../@clerk/backend/src/api/request.ts:278:21)
at async HandshakeService.getCookiesFromHandshake (.../@clerk/backend/src/tokens/handshake.ts:187:34)
at async HandshakeService.resolveHandshake (.../@clerk/backend/src/tokens/handshake.ts:220:26)
at async authenticateRequestWithTokenInCookie (.../@clerk/backend/src/tokens/request.ts:457:16)
at async Object.<anonymous> (.../@clerk/fastify/src/withClerkMiddleware.ts:88:26)

Quick solution: strip/ignore cookies/queries when the config is enableHandshake: false.

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@wobsoriano

Copy link
Copy Markdown
Member

@jorgevrgs thanks, generated another snapshot for you

npm i @clerk/fastify@3.1.28-snapshot.v20260516134813 --save-exact

@wobsorianowobsoriano changed the title chore(fastify): Use runtime keys for auth client and add enableHandshake optionchore(fastify): Use runtime keys for auth client and explicit opt-out handshake optionMay 16, 2026
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Where are we at with this one? Don't want it to linger as a single customer hotfix

…ecret-handshake
# Conflicts:
#	.changeset/silent-fastify-handshakes.md
#	packages/fastify/src/__tests__/withClerkMiddleware.test.ts
#	packages/fastify/src/clerkPlugin.ts
#	packages/fastify/src/withClerkMiddleware.ts
@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-02T00:50:48.809Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes2
🟢 Additions0

🤖 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/fastify

Current version: 3.1.32
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change to ClerkFastifyOptions is the addition of an optional property enableHandshake?: boolean. Adding an optional field to an intersection/object type used as a plugin options input is non-breaking: existing consumers who don't pass enableHandshake continue to compile and run correctly, and no previously valid consumer code becomes invalid.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change is the addition of a new optional field enableHandshake?: boolean to ClerkFastifyOptions. Since it is optional, existing consumer code that constructs or uses this type without the field continues to compile and run correctly. Adding an optional property to an intersection type is non-breaking for callers.


Report generated by Break Check

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @wobsoriano - the snapshot version command generated the following package versions:

PackageVersion
@clerk/astro3.3.3-snapshot.v20260602010447
@clerk/backend3.5.0-snapshot.v20260602010447
@clerk/chrome-extension3.1.32-snapshot.v20260602010447
@clerk/clerk-js6.13.1-snapshot.v20260602010447
@clerk/expo3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys1.1.1-snapshot.v20260602010447
@clerk/express2.1.23-snapshot.v20260602010447
@clerk/fastify3.2.0-snapshot.v20260602010447
@clerk/hono0.1.33-snapshot.v20260602010447
@clerk/localizations4.7.1-snapshot.v20260602010447
@clerk/msw0.0.31-snapshot.v20260602010447
@clerk/nextjs7.4.3-snapshot.v20260602010447
@clerk/nuxt2.5.3-snapshot.v20260602010447
@clerk/react6.7.3-snapshot.v20260602010447
@clerk/react-router3.3.3-snapshot.v20260602010447
@clerk/shared4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start1.3.3-snapshot.v20260602010447
@clerk/testing2.0.35-snapshot.v20260602010447
@clerk/ui1.14.1-snapshot.v20260602010447
@clerk/upgrade2.0.3-snapshot.v20260602010447
@clerk/vue2.3.3-snapshot.v20260602010447

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/backend

npm i @clerk/backend@3.5.0-snapshot.v20260602010447 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.32-snapshot.v20260602010447 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.13.1-snapshot.v20260602010447 --save-exact

@clerk/expo

npm i @clerk/expo@3.3.1-snapshot.v20260602010447 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.1-snapshot.v20260602010447 --save-exact

@clerk/express

npm i @clerk/express@2.1.23-snapshot.v20260602010447 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.2.0-snapshot.v20260602010447 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.33-snapshot.v20260602010447 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.7.1-snapshot.v20260602010447 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.31-snapshot.v20260602010447 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.4.3-snapshot.v20260602010447 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.5.3-snapshot.v20260602010447 --save-exact

@clerk/react

npm i @clerk/react@6.7.3-snapshot.v20260602010447 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/shared

npm i @clerk/shared@4.14.1-snapshot.v20260602010447 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.3.3-snapshot.v20260602010447 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.35-snapshot.v20260602010447 --save-exact

@clerk/ui

npm i @clerk/ui@1.14.1-snapshot.v20260602010447 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260602010447 --save-exact

@clerk/vue

npm i @clerk/vue@2.3.3-snapshot.v20260602010447 --save-exact

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Bumping this again

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Another bump here - this is debt that I feel that we need to clear out 😬

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jescalan@wobsoriano@jorgevrgs@jeremy-clerk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option by jescalan · Pull Request #8560 · clerk/javascript · GitHub
Skip to content

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option - #8560

Open
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Open

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option#8560
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalanjescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • create server-side auth clients from resolved runtime middleware options before calling authenticateRequest
  • cover Fastify, Express, Astro, Nuxt, React Router, and TanStack Start so nonce handshake payload exchange can use keys passed directly to framework middleware
  • add focused regression coverage for runtime key propagation into client construction

Context

Forced handshake nonce transport stores a short __clerk_handshake_nonce instead of the large __clerk_handshake payload. Server SDKs then need to exchange that nonce through the Backend API client attached to authenticateRequest.

Several framework wrappers passed runtime secretKey/publishableKey into authenticateRequest, but constructed the request client from environment defaults or earlier unresolved options. That means apps loading keys asynchronously and passing them into middleware could authenticate some paths with the runtime key while nonce payload exchange still used a client created without that key.

Hono and Next.js already build the client from runtime options, so no patch was needed there.

Performance

This patch avoids adding a new per-request client construction path in the common static-key cases:

  • Fastify creates the Clerk client once when clerkPlugin() registers middleware.
  • Express creates the default client once when clerkMiddleware() is created for static options. The callback form can still create a middleware/client per request, but that was already how callback options worked.
  • Astro, Nuxt, React Router, and TanStack Start already created the server Clerk client inside the request path before this PR. This patch passes the resolved options into that existing construction instead of introducing another client creation.
  • createClerkClient() itself does not perform network I/O; it builds the Backend API resource client, authenticateRequest closure, and telemetry collector. The nonce exchange network call only happens when authenticateRequest reaches forced handshake nonce handling.

Testing

  • pnpm -C packages/fastify exec vitest run src/__tests__/withClerkMiddleware.test.ts
  • pnpm -C packages/express exec vitest run src/__tests__/clerkMiddleware.test.ts -t "builds a per-middleware ClerkClient with runtime keys"
  • pnpm -C packages/react-router exec vitest run src/server/__tests__/clerkMiddleware.test.ts
  • pnpm -C packages/nuxt exec vitest run src/runtime/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/astro exec vitest run src/server/__tests__/clerk-client.test.ts
  • pnpm -C packages/tanstack-react-start exec vitest run src/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/fastify build
  • pnpm -C packages/express build
  • pnpm -C packages/fastify lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/express lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/astro lint (passes with existing warnings)
  • git diff --check

Notes

  • Full Express middleware tests currently hit the local backend/shared export mismatch: getAutoProxyUrlFromEnvironment is not a function.
  • React Router, Nuxt, Astro, and TanStack builds/lints still hit existing local package-linkage/DTS issues unrelated to this patch (missing local @clerk/react/@clerk/vue exports or declarations). Their JS builds reached the compile stage before those existing declaration/linkage failures.

@vercel

vercelBot commented May 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 2, 2026 12:47am

Request Review

@changeset-bot

changeset-botBot commented May 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e5bb059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
NameType
@clerk/fastifyMinor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jescalanjescalan changed the title [codex] fix(fastify): use runtime keys for auth clientfix(fastify): use runtime keys for auth clientMay 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@jeremy-clerkjeremy-clerk 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.

LGTM

@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR initializes a single Clerk client at middleware setup using resolved runtime keys, adds an optional enableHandshake flag to control handshake/redirect handling, strips handshake cookies/query params when disabled, and attaches the Clerk client to requests as request.clerk. Type declarations and the plugin decoration are updated accordingly, and tests were added to verify runtime key usage, enableHandshake behavior, handshake cookie stripping, and request.clerk exposure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe title accurately describes the main changes: implementing runtime key usage for auth client initialization and adding an explicit handshake opt-out option to Fastify.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the problem context, solution, performance implications, testing, and notes about related packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-newBot commented May 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e5bb059

@jescalan
jescalanforce-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99CompareMay 15, 2026 15:58
@jescalanjescalan changed the title fix(fastify): use runtime keys for auth client[codex] fix framework runtime keys for nonce handshakesMay 15, 2026
@jescalan
jescalan marked this pull request as draft May 15, 2026 15:59
@wobsoriano

Copy link
Copy Markdown
Member

@copilot can you add changes from #8563 to this PR?

CopilotAI commented May 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you add changes from #8563 to this PR?

Done — I pulled in the changes from #8563 in commits 19f3473, 0cc1e3c, and 60312d1, and added a follow-up fix for the React Router CI type error in 9243a0a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /usr/local/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@wobsoriano
wobsoriano marked this pull request as ready for review May 16, 2026 00:14
@jorgevrgs

Copy link
Copy Markdown

It looks like enableHandshake: false doesn't prevent the API to request GET /v1/client/handshake always if the cookies __clerk_handshake and/or __clerk_handshake_nonce exist. That causes a noisy log:

ERROR Clerk: HandshakeService: error getting handshake payload: ClerkAPIResponseError: Not Found
at ClientAPI.request (.../@clerk/backend/src/api/request.ts:278:21)
at async HandshakeService.getCookiesFromHandshake (.../@clerk/backend/src/tokens/handshake.ts:187:34)
at async HandshakeService.resolveHandshake (.../@clerk/backend/src/tokens/handshake.ts:220:26)
at async authenticateRequestWithTokenInCookie (.../@clerk/backend/src/tokens/request.ts:457:16)
at async Object.<anonymous> (.../@clerk/fastify/src/withClerkMiddleware.ts:88:26)

Quick solution: strip/ignore cookies/queries when the config is enableHandshake: false.

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@wobsoriano

Copy link
Copy Markdown
Member

@jorgevrgs thanks, generated another snapshot for you

npm i @clerk/fastify@3.1.28-snapshot.v20260516134813 --save-exact

@wobsorianowobsoriano changed the title chore(fastify): Use runtime keys for auth client and add enableHandshake optionchore(fastify): Use runtime keys for auth client and explicit opt-out handshake optionMay 16, 2026
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Where are we at with this one? Don't want it to linger as a single customer hotfix

…ecret-handshake
# Conflicts:
#	.changeset/silent-fastify-handshakes.md
#	packages/fastify/src/__tests__/withClerkMiddleware.test.ts
#	packages/fastify/src/clerkPlugin.ts
#	packages/fastify/src/withClerkMiddleware.ts
@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-02T00:50:48.809Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes2
🟢 Additions0

🤖 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/fastify

Current version: 3.1.32
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change to ClerkFastifyOptions is the addition of an optional property enableHandshake?: boolean. Adding an optional field to an intersection/object type used as a plugin options input is non-breaking: existing consumers who don't pass enableHandshake continue to compile and run correctly, and no previously valid consumer code becomes invalid.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change is the addition of a new optional field enableHandshake?: boolean to ClerkFastifyOptions. Since it is optional, existing consumer code that constructs or uses this type without the field continues to compile and run correctly. Adding an optional property to an intersection type is non-breaking for callers.


Report generated by Break Check

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @wobsoriano - the snapshot version command generated the following package versions:

PackageVersion
@clerk/astro3.3.3-snapshot.v20260602010447
@clerk/backend3.5.0-snapshot.v20260602010447
@clerk/chrome-extension3.1.32-snapshot.v20260602010447
@clerk/clerk-js6.13.1-snapshot.v20260602010447
@clerk/expo3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys1.1.1-snapshot.v20260602010447
@clerk/express2.1.23-snapshot.v20260602010447
@clerk/fastify3.2.0-snapshot.v20260602010447
@clerk/hono0.1.33-snapshot.v20260602010447
@clerk/localizations4.7.1-snapshot.v20260602010447
@clerk/msw0.0.31-snapshot.v20260602010447
@clerk/nextjs7.4.3-snapshot.v20260602010447
@clerk/nuxt2.5.3-snapshot.v20260602010447
@clerk/react6.7.3-snapshot.v20260602010447
@clerk/react-router3.3.3-snapshot.v20260602010447
@clerk/shared4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start1.3.3-snapshot.v20260602010447
@clerk/testing2.0.35-snapshot.v20260602010447
@clerk/ui1.14.1-snapshot.v20260602010447
@clerk/upgrade2.0.3-snapshot.v20260602010447
@clerk/vue2.3.3-snapshot.v20260602010447

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/backend

npm i @clerk/backend@3.5.0-snapshot.v20260602010447 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.32-snapshot.v20260602010447 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.13.1-snapshot.v20260602010447 --save-exact

@clerk/expo

npm i @clerk/expo@3.3.1-snapshot.v20260602010447 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.1-snapshot.v20260602010447 --save-exact

@clerk/express

npm i @clerk/express@2.1.23-snapshot.v20260602010447 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.2.0-snapshot.v20260602010447 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.33-snapshot.v20260602010447 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.7.1-snapshot.v20260602010447 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.31-snapshot.v20260602010447 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.4.3-snapshot.v20260602010447 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.5.3-snapshot.v20260602010447 --save-exact

@clerk/react

npm i @clerk/react@6.7.3-snapshot.v20260602010447 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/shared

npm i @clerk/shared@4.14.1-snapshot.v20260602010447 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.3.3-snapshot.v20260602010447 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.35-snapshot.v20260602010447 --save-exact

@clerk/ui

npm i @clerk/ui@1.14.1-snapshot.v20260602010447 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260602010447 --save-exact

@clerk/vue

npm i @clerk/vue@2.3.3-snapshot.v20260602010447 --save-exact

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Bumping this again

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Another bump here - this is debt that I feel that we need to clear out 😬

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jescalan@wobsoriano@jorgevrgs@jeremy-clerk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option by jescalan · Pull Request #8560 · clerk/javascript · GitHub
Skip to content

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option - #8560

Open
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Open

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option#8560
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalanjescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • create server-side auth clients from resolved runtime middleware options before calling authenticateRequest
  • cover Fastify, Express, Astro, Nuxt, React Router, and TanStack Start so nonce handshake payload exchange can use keys passed directly to framework middleware
  • add focused regression coverage for runtime key propagation into client construction

Context

Forced handshake nonce transport stores a short __clerk_handshake_nonce instead of the large __clerk_handshake payload. Server SDKs then need to exchange that nonce through the Backend API client attached to authenticateRequest.

Several framework wrappers passed runtime secretKey/publishableKey into authenticateRequest, but constructed the request client from environment defaults or earlier unresolved options. That means apps loading keys asynchronously and passing them into middleware could authenticate some paths with the runtime key while nonce payload exchange still used a client created without that key.

Hono and Next.js already build the client from runtime options, so no patch was needed there.

Performance

This patch avoids adding a new per-request client construction path in the common static-key cases:

  • Fastify creates the Clerk client once when clerkPlugin() registers middleware.
  • Express creates the default client once when clerkMiddleware() is created for static options. The callback form can still create a middleware/client per request, but that was already how callback options worked.
  • Astro, Nuxt, React Router, and TanStack Start already created the server Clerk client inside the request path before this PR. This patch passes the resolved options into that existing construction instead of introducing another client creation.
  • createClerkClient() itself does not perform network I/O; it builds the Backend API resource client, authenticateRequest closure, and telemetry collector. The nonce exchange network call only happens when authenticateRequest reaches forced handshake nonce handling.

Testing

  • pnpm -C packages/fastify exec vitest run src/__tests__/withClerkMiddleware.test.ts
  • pnpm -C packages/express exec vitest run src/__tests__/clerkMiddleware.test.ts -t "builds a per-middleware ClerkClient with runtime keys"
  • pnpm -C packages/react-router exec vitest run src/server/__tests__/clerkMiddleware.test.ts
  • pnpm -C packages/nuxt exec vitest run src/runtime/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/astro exec vitest run src/server/__tests__/clerk-client.test.ts
  • pnpm -C packages/tanstack-react-start exec vitest run src/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/fastify build
  • pnpm -C packages/express build
  • pnpm -C packages/fastify lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/express lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/astro lint (passes with existing warnings)
  • git diff --check

Notes

  • Full Express middleware tests currently hit the local backend/shared export mismatch: getAutoProxyUrlFromEnvironment is not a function.
  • React Router, Nuxt, Astro, and TanStack builds/lints still hit existing local package-linkage/DTS issues unrelated to this patch (missing local @clerk/react/@clerk/vue exports or declarations). Their JS builds reached the compile stage before those existing declaration/linkage failures.

@vercel

vercelBot commented May 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 2, 2026 12:47am

Request Review

@changeset-bot

changeset-botBot commented May 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e5bb059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
NameType
@clerk/fastifyMinor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jescalanjescalan changed the title [codex] fix(fastify): use runtime keys for auth clientfix(fastify): use runtime keys for auth clientMay 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@jeremy-clerkjeremy-clerk 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.

LGTM

@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR initializes a single Clerk client at middleware setup using resolved runtime keys, adds an optional enableHandshake flag to control handshake/redirect handling, strips handshake cookies/query params when disabled, and attaches the Clerk client to requests as request.clerk. Type declarations and the plugin decoration are updated accordingly, and tests were added to verify runtime key usage, enableHandshake behavior, handshake cookie stripping, and request.clerk exposure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe title accurately describes the main changes: implementing runtime key usage for auth client initialization and adding an explicit handshake opt-out option to Fastify.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the problem context, solution, performance implications, testing, and notes about related packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-newBot commented May 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e5bb059

@jescalan
jescalanforce-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99CompareMay 15, 2026 15:58
@jescalanjescalan changed the title fix(fastify): use runtime keys for auth client[codex] fix framework runtime keys for nonce handshakesMay 15, 2026
@jescalan
jescalan marked this pull request as draft May 15, 2026 15:59
@wobsoriano

Copy link
Copy Markdown
Member

@copilot can you add changes from #8563 to this PR?

CopilotAI commented May 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you add changes from #8563 to this PR?

Done — I pulled in the changes from #8563 in commits 19f3473, 0cc1e3c, and 60312d1, and added a follow-up fix for the React Router CI type error in 9243a0a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /usr/local/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@wobsoriano
wobsoriano marked this pull request as ready for review May 16, 2026 00:14
@jorgevrgs

Copy link
Copy Markdown

It looks like enableHandshake: false doesn't prevent the API to request GET /v1/client/handshake always if the cookies __clerk_handshake and/or __clerk_handshake_nonce exist. That causes a noisy log:

ERROR Clerk: HandshakeService: error getting handshake payload: ClerkAPIResponseError: Not Found
at ClientAPI.request (.../@clerk/backend/src/api/request.ts:278:21)
at async HandshakeService.getCookiesFromHandshake (.../@clerk/backend/src/tokens/handshake.ts:187:34)
at async HandshakeService.resolveHandshake (.../@clerk/backend/src/tokens/handshake.ts:220:26)
at async authenticateRequestWithTokenInCookie (.../@clerk/backend/src/tokens/request.ts:457:16)
at async Object.<anonymous> (.../@clerk/fastify/src/withClerkMiddleware.ts:88:26)

Quick solution: strip/ignore cookies/queries when the config is enableHandshake: false.

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@wobsoriano

Copy link
Copy Markdown
Member

@jorgevrgs thanks, generated another snapshot for you

npm i @clerk/fastify@3.1.28-snapshot.v20260516134813 --save-exact

@wobsorianowobsoriano changed the title chore(fastify): Use runtime keys for auth client and add enableHandshake optionchore(fastify): Use runtime keys for auth client and explicit opt-out handshake optionMay 16, 2026
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Where are we at with this one? Don't want it to linger as a single customer hotfix

…ecret-handshake
# Conflicts:
#	.changeset/silent-fastify-handshakes.md
#	packages/fastify/src/__tests__/withClerkMiddleware.test.ts
#	packages/fastify/src/clerkPlugin.ts
#	packages/fastify/src/withClerkMiddleware.ts
@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-02T00:50:48.809Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes2
🟢 Additions0

🤖 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/fastify

Current version: 3.1.32
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change to ClerkFastifyOptions is the addition of an optional property enableHandshake?: boolean. Adding an optional field to an intersection/object type used as a plugin options input is non-breaking: existing consumers who don't pass enableHandshake continue to compile and run correctly, and no previously valid consumer code becomes invalid.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change is the addition of a new optional field enableHandshake?: boolean to ClerkFastifyOptions. Since it is optional, existing consumer code that constructs or uses this type without the field continues to compile and run correctly. Adding an optional property to an intersection type is non-breaking for callers.


Report generated by Break Check

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @wobsoriano - the snapshot version command generated the following package versions:

PackageVersion
@clerk/astro3.3.3-snapshot.v20260602010447
@clerk/backend3.5.0-snapshot.v20260602010447
@clerk/chrome-extension3.1.32-snapshot.v20260602010447
@clerk/clerk-js6.13.1-snapshot.v20260602010447
@clerk/expo3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys1.1.1-snapshot.v20260602010447
@clerk/express2.1.23-snapshot.v20260602010447
@clerk/fastify3.2.0-snapshot.v20260602010447
@clerk/hono0.1.33-snapshot.v20260602010447
@clerk/localizations4.7.1-snapshot.v20260602010447
@clerk/msw0.0.31-snapshot.v20260602010447
@clerk/nextjs7.4.3-snapshot.v20260602010447
@clerk/nuxt2.5.3-snapshot.v20260602010447
@clerk/react6.7.3-snapshot.v20260602010447
@clerk/react-router3.3.3-snapshot.v20260602010447
@clerk/shared4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start1.3.3-snapshot.v20260602010447
@clerk/testing2.0.35-snapshot.v20260602010447
@clerk/ui1.14.1-snapshot.v20260602010447
@clerk/upgrade2.0.3-snapshot.v20260602010447
@clerk/vue2.3.3-snapshot.v20260602010447

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/backend

npm i @clerk/backend@3.5.0-snapshot.v20260602010447 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.32-snapshot.v20260602010447 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.13.1-snapshot.v20260602010447 --save-exact

@clerk/expo

npm i @clerk/expo@3.3.1-snapshot.v20260602010447 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.1-snapshot.v20260602010447 --save-exact

@clerk/express

npm i @clerk/express@2.1.23-snapshot.v20260602010447 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.2.0-snapshot.v20260602010447 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.33-snapshot.v20260602010447 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.7.1-snapshot.v20260602010447 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.31-snapshot.v20260602010447 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.4.3-snapshot.v20260602010447 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.5.3-snapshot.v20260602010447 --save-exact

@clerk/react

npm i @clerk/react@6.7.3-snapshot.v20260602010447 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/shared

npm i @clerk/shared@4.14.1-snapshot.v20260602010447 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.3.3-snapshot.v20260602010447 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.35-snapshot.v20260602010447 --save-exact

@clerk/ui

npm i @clerk/ui@1.14.1-snapshot.v20260602010447 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260602010447 --save-exact

@clerk/vue

npm i @clerk/vue@2.3.3-snapshot.v20260602010447 --save-exact

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Bumping this again

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Another bump here - this is debt that I feel that we need to clear out 😬

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jescalan@wobsoriano@jorgevrgs@jeremy-clerk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option by jescalan · Pull Request #8560 · clerk/javascript · GitHub
Skip to content

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option - #8560

Open
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Open

chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option#8560
jescalan wants to merge 17 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalanjescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • create server-side auth clients from resolved runtime middleware options before calling authenticateRequest
  • cover Fastify, Express, Astro, Nuxt, React Router, and TanStack Start so nonce handshake payload exchange can use keys passed directly to framework middleware
  • add focused regression coverage for runtime key propagation into client construction

Context

Forced handshake nonce transport stores a short __clerk_handshake_nonce instead of the large __clerk_handshake payload. Server SDKs then need to exchange that nonce through the Backend API client attached to authenticateRequest.

Several framework wrappers passed runtime secretKey/publishableKey into authenticateRequest, but constructed the request client from environment defaults or earlier unresolved options. That means apps loading keys asynchronously and passing them into middleware could authenticate some paths with the runtime key while nonce payload exchange still used a client created without that key.

Hono and Next.js already build the client from runtime options, so no patch was needed there.

Performance

This patch avoids adding a new per-request client construction path in the common static-key cases:

  • Fastify creates the Clerk client once when clerkPlugin() registers middleware.
  • Express creates the default client once when clerkMiddleware() is created for static options. The callback form can still create a middleware/client per request, but that was already how callback options worked.
  • Astro, Nuxt, React Router, and TanStack Start already created the server Clerk client inside the request path before this PR. This patch passes the resolved options into that existing construction instead of introducing another client creation.
  • createClerkClient() itself does not perform network I/O; it builds the Backend API resource client, authenticateRequest closure, and telemetry collector. The nonce exchange network call only happens when authenticateRequest reaches forced handshake nonce handling.

Testing

  • pnpm -C packages/fastify exec vitest run src/__tests__/withClerkMiddleware.test.ts
  • pnpm -C packages/express exec vitest run src/__tests__/clerkMiddleware.test.ts -t "builds a per-middleware ClerkClient with runtime keys"
  • pnpm -C packages/react-router exec vitest run src/server/__tests__/clerkMiddleware.test.ts
  • pnpm -C packages/nuxt exec vitest run src/runtime/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/astro exec vitest run src/server/__tests__/clerk-client.test.ts
  • pnpm -C packages/tanstack-react-start exec vitest run src/server/__tests__/clerkClient.test.ts
  • pnpm -C packages/fastify build
  • pnpm -C packages/express build
  • pnpm -C packages/fastify lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/express lint (passes with existing no-misused-promises warnings)
  • pnpm -C packages/astro lint (passes with existing warnings)
  • git diff --check

Notes

  • Full Express middleware tests currently hit the local backend/shared export mismatch: getAutoProxyUrlFromEnvironment is not a function.
  • React Router, Nuxt, Astro, and TanStack builds/lints still hit existing local package-linkage/DTS issues unrelated to this patch (missing local @clerk/react/@clerk/vue exports or declarations). Their JS builds reached the compile stage before those existing declaration/linkage failures.

@vercel

vercelBot commented May 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJun 2, 2026 12:47am

Request Review

@changeset-bot

changeset-botBot commented May 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e5bb059

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
NameType
@clerk/fastifyMinor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@jescalanjescalan changed the title [codex] fix(fastify): use runtime keys for auth clientfix(fastify): use runtime keys for auth clientMay 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
ContributorAuthor

!snapshot

@jeremy-clerkjeremy-clerk 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.

LGTM

@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR initializes a single Clerk client at middleware setup using resolved runtime keys, adds an optional enableHandshake flag to control handshake/redirect handling, strips handshake cookies/query params when disabled, and attaches the Clerk client to requests as request.clerk. Type declarations and the plugin decoration are updated accordingly, and tests were added to verify runtime key usage, enableHandshake behavior, handshake cookie stripping, and request.clerk exposure.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe title accurately describes the main changes: implementing runtime key usage for auth client initialization and adding an explicit handshake opt-out option to Fastify.
Description check✅ PassedThe description is comprehensive and directly related to the changeset, explaining the problem context, solution, performance implications, testing, and notes about related packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-newBot commented May 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: e5bb059

@jescalan
jescalanforce-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99CompareMay 15, 2026 15:58
@jescalanjescalan changed the title fix(fastify): use runtime keys for auth client[codex] fix framework runtime keys for nonce handshakesMay 15, 2026
@jescalan
jescalan marked this pull request as draft May 15, 2026 15:59
@wobsoriano

Copy link
Copy Markdown
Member

@copilot can you add changes from #8563 to this PR?

CopilotAI commented May 15, 2026

Copy link
Copy Markdown
Contributor

@copilot can you add changes from #8563 to this PR?

Done — I pulled in the changes from #8563 in commits 19f3473, 0cc1e3c, and 60312d1, and added a follow-up fix for the React Router CI type error in 9243a0a.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.cypress.io
    • Triggering command: /usr/local/bin/node node index.js --exec install (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@wobsoriano
wobsoriano marked this pull request as ready for review May 16, 2026 00:14
@jorgevrgs

Copy link
Copy Markdown

It looks like enableHandshake: false doesn't prevent the API to request GET /v1/client/handshake always if the cookies __clerk_handshake and/or __clerk_handshake_nonce exist. That causes a noisy log:

ERROR Clerk: HandshakeService: error getting handshake payload: ClerkAPIResponseError: Not Found
at ClientAPI.request (.../@clerk/backend/src/api/request.ts:278:21)
at async HandshakeService.getCookiesFromHandshake (.../@clerk/backend/src/tokens/handshake.ts:187:34)
at async HandshakeService.resolveHandshake (.../@clerk/backend/src/tokens/handshake.ts:220:26)
at async authenticateRequestWithTokenInCookie (.../@clerk/backend/src/tokens/request.ts:457:16)
at async Object.<anonymous> (.../@clerk/fastify/src/withClerkMiddleware.ts:88:26)

Quick solution: strip/ignore cookies/queries when the config is enableHandshake: false.

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@wobsoriano

Copy link
Copy Markdown
Member

@jorgevrgs thanks, generated another snapshot for you

npm i @clerk/fastify@3.1.28-snapshot.v20260516134813 --save-exact

@wobsorianowobsoriano changed the title chore(fastify): Use runtime keys for auth client and add enableHandshake optionchore(fastify): Use runtime keys for auth client and explicit opt-out handshake optionMay 16, 2026
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

This comment has been minimized.

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Where are we at with this one? Don't want it to linger as a single customer hotfix

…ecret-handshake
# Conflicts:
#	.changeset/silent-fastify-handshakes.md
#	packages/fastify/src/__tests__/withClerkMiddleware.test.ts
#	packages/fastify/src/clerkPlugin.ts
#	packages/fastify/src/withClerkMiddleware.ts
@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-02T00:50:48.809Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes2
🟢 Additions0

🤖 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/fastify

Current version: 3.1.32
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change to ClerkFastifyOptions is the addition of an optional property enableHandshake?: boolean. Adding an optional field to an intersection/object type used as a plugin options input is non-breaking: existing consumers who don't pass enableHandshake continue to compile and run correctly, and no previously valid consumer code becomes invalid.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
 type ClerkFastifyOptions = ClerkOptions & {
hookName?: (typeof ALLOWED_HOOKS)[number];
frontendApiProxy?: FrontendApiProxyOptions;
+ enableHandshake?: boolean;
};

Static analyzer: Breaking change in type alias ClerkFastifyOptions: Type changed: import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…import("@clerk/backend").ClerkOptions&{hookName?:(typeof import("@clerk/fastify").~ALLOWED_HOOKS)[number];frontendApiPr…

🤖 AI review (reclassified as non-breaking) (95%): The only change is the addition of a new optional field enableHandshake?: boolean to ClerkFastifyOptions. Since it is optional, existing consumer code that constructs or uses this type without the field continues to compile and run correctly. Adding an optional property to an intersection type is non-breaking for callers.


Report generated by Break Check

@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@github-actions

Copy link
Copy Markdown
Contributor

Hey @wobsoriano - the snapshot version command generated the following package versions:

PackageVersion
@clerk/astro3.3.3-snapshot.v20260602010447
@clerk/backend3.5.0-snapshot.v20260602010447
@clerk/chrome-extension3.1.32-snapshot.v20260602010447
@clerk/clerk-js6.13.1-snapshot.v20260602010447
@clerk/expo3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys1.1.1-snapshot.v20260602010447
@clerk/express2.1.23-snapshot.v20260602010447
@clerk/fastify3.2.0-snapshot.v20260602010447
@clerk/hono0.1.33-snapshot.v20260602010447
@clerk/localizations4.7.1-snapshot.v20260602010447
@clerk/msw0.0.31-snapshot.v20260602010447
@clerk/nextjs7.4.3-snapshot.v20260602010447
@clerk/nuxt2.5.3-snapshot.v20260602010447
@clerk/react6.7.3-snapshot.v20260602010447
@clerk/react-router3.3.3-snapshot.v20260602010447
@clerk/shared4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start1.3.3-snapshot.v20260602010447
@clerk/testing2.0.35-snapshot.v20260602010447
@clerk/ui1.14.1-snapshot.v20260602010447
@clerk/upgrade2.0.3-snapshot.v20260602010447
@clerk/vue2.3.3-snapshot.v20260602010447

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/backend

npm i @clerk/backend@3.5.0-snapshot.v20260602010447 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.32-snapshot.v20260602010447 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.13.1-snapshot.v20260602010447 --save-exact

@clerk/expo

npm i @clerk/expo@3.3.1-snapshot.v20260602010447 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.1.1-snapshot.v20260602010447 --save-exact

@clerk/express

npm i @clerk/express@2.1.23-snapshot.v20260602010447 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.2.0-snapshot.v20260602010447 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.33-snapshot.v20260602010447 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.7.1-snapshot.v20260602010447 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.31-snapshot.v20260602010447 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.4.3-snapshot.v20260602010447 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.5.3-snapshot.v20260602010447 --save-exact

@clerk/react

npm i @clerk/react@6.7.3-snapshot.v20260602010447 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.3.3-snapshot.v20260602010447 --save-exact

@clerk/shared

npm i @clerk/shared@4.14.1-snapshot.v20260602010447 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.3.3-snapshot.v20260602010447 --save-exact

@clerk/testing

npm i @clerk/testing@2.0.35-snapshot.v20260602010447 --save-exact

@clerk/ui

npm i @clerk/ui@1.14.1-snapshot.v20260602010447 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.3-snapshot.v20260602010447 --save-exact

@clerk/vue

npm i @clerk/vue@2.3.3-snapshot.v20260602010447 --save-exact

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Bumping this again

@jescalan

Copy link
Copy Markdown
ContributorAuthor

Another bump here - this is debt that I feel that we need to clear out 😬

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@jescalan@wobsoriano@jorgevrgs@jeremy-clerk