Skip to content

fix(fastify): Add __internal_enableHandshake option to skip handshake flow - #8560

Merged
wobsoriano merged 3 commits into
mainfrom
fix/fastify-runtime-secret-handshake
Sep 1, 2026
Merged

fix(fastify): Add __internal_enableHandshake option to skip handshake flow#8560
wobsoriano merged 3 commits into
mainfrom
fix/fastify-runtime-secret-handshake

Conversation

@jescalan

@jescalan jescalan commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add an internal __internal_enableHandshake option to clerkPlugin() (defaults to true)
  • when set to false, strip handshake cookies (__clerk_handshake, __clerk_handshake_nonce) and query params before calling authenticateRequest, and skip handshake redirects
  • keep dev-browser handshake redirects (dev-browser-missing, dev-browser-sync) even when disabled, since development instances cannot establish auth state without them
  • fall back to a signed-out auth object when a handshake redirect is skipped (a handshake state's toAuth() is null), and drop the already-applied Location/Cache-Control headers from the reply
  • add focused regression coverage for the disabled flow, the dev-browser exception, and the default behavior

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.

On an API-only backend that cannot deliver Set-Cookie headers back to the browser (e.g. Fastify on Lambda behind API Gateway, where multi-value Set-Cookie headers are dropped), the browser can never clear that nonce cookie. Every subsequent request replays the already-consumed nonce, and the exchange returns 404 resource_not_found, logging Clerk: HandshakeService: error getting handshake payload on each request. authenticateRequest already tolerates the failed exchange and falls through to cookie verification, so disabling the handshake removes the per-request error noise, the wasted Backend API call, and handshake redirects that an API client can never complete.

The option is __internal_-prefixed rather than public because @clerk/express deprecated (#6499) and removed (#7926) its public enableHandshake option, and we don't want to reintroduce public API surface for this. The affected customer has been running the equivalent behavior in production via snapshot since May 22 with the errors fully gone.

Notes

  • The runtime-keys client construction changes originally on this branch shipped separately in fix(fastify): Use runtime keys for Clerk client #8640, so the branch was rebuilt on main and now contains only the handshake option.
  • The dev-browser reasons are compared as string literals because AuthErrorReason is not exported from @clerk/backend/internal; exporting it is a one-line @clerk/backend change if preferred.

@vercel

vercel Bot commented May 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
clerk-js-sandbox Ready Ready Preview Sep 1, 2026 5:14pm UTC
swingset Ready Ready Preview Sep 1, 2026 5:14pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented May 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 386bcff

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

This PR includes changesets to release 1 package
Name Type
@clerk/fastify Patch

Not sure what this means? Click here to learn what changesets are.

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

@jescalan jescalan changed the title [codex] fix(fastify): use runtime keys for auth client fix(fastify): use runtime keys for auth client May 15, 2026
@jescalan
jescalan marked this pull request as ready for review May 15, 2026 15:44
@jescalan
jescalan requested a review from jeremy-clerk May 15, 2026 15:44
@jescalan

Copy link
Copy Markdown
Contributor Author

!snapshot

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

coderabbitai Bot 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 name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The 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 ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The pull request title 'chore(fastify): Explicit opt-out handshake option' describes only one aspect of the changeset. The PR objectives indicate two primary goals: (1) using runtime keys for auth cli…
Full details: Title check

Explanation

The pull request title 'chore(fastify): Explicit opt-out handshake option' describes only one aspect of the changeset. The PR objectives indicate two primary goals: (1) using runtime keys for auth client construction to fix authentication failures when secrets load at runtime, and (2) adding an explicit opt-out handshake option. The title captures the second objective but omits the first, which is the core bug fix documented in the changeset entry. The title is related to the changeset but does not convey the main point.

  • Fix all pre-merge checks with AI

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.

@github-actions

This comment has been minimized.

@pkg-pr-new

pkg-pr-new Bot 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/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@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: 3823adc

@jescalan
jescalan force-pushed the fix/fastify-runtime-secret-handshake branch from 71d3ff7 to 25efe99 Compare May 15, 2026 15:58
@jescalan jescalan changed the title fix(fastify): use runtime keys for auth client [codex] fix framework runtime keys for nonce handshakes May 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?

Copilot AI 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

Copy link
Copy Markdown
Member

!snapshot

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

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

Package Version
@clerk/astro 3.3.3-snapshot.v20260602010447
@clerk/backend 3.5.0-snapshot.v20260602010447
@clerk/chrome-extension 3.1.32-snapshot.v20260602010447
@clerk/clerk-js 6.13.1-snapshot.v20260602010447
@clerk/expo 3.3.1-snapshot.v20260602010447
@clerk/expo-passkeys 1.1.1-snapshot.v20260602010447
@clerk/express 2.1.23-snapshot.v20260602010447
@clerk/fastify 3.2.0-snapshot.v20260602010447
@clerk/hono 0.1.33-snapshot.v20260602010447
@clerk/localizations 4.7.1-snapshot.v20260602010447
@clerk/msw 0.0.31-snapshot.v20260602010447
@clerk/nextjs 7.4.3-snapshot.v20260602010447
@clerk/nuxt 2.5.3-snapshot.v20260602010447
@clerk/react 6.7.3-snapshot.v20260602010447
@clerk/react-router 3.3.3-snapshot.v20260602010447
@clerk/shared 4.14.1-snapshot.v20260602010447
@clerk/tanstack-react-start 1.3.3-snapshot.v20260602010447
@clerk/testing 2.0.35-snapshot.v20260602010447
@clerk/ui 1.14.1-snapshot.v20260602010447
@clerk/upgrade 2.0.3-snapshot.v20260602010447
@clerk/vue 2.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
Contributor Author

Bumping this again

@jescalan

Copy link
Copy Markdown
Contributor Author

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

@wobsoriano wobsoriano changed the title chore(fastify): Use runtime keys for auth client and explicit opt-out handshake option chore(fastify): Explicit opt-out handshake option Sep 1, 2026
@wobsoriano
wobsoriano force-pushed the fix/fastify-runtime-secret-handshake branch 2 times, most recently from e5bb059 to 08cffa5 Compare September 1, 2026 16:56
@wobsoriano wobsoriano changed the title chore(fastify): Explicit opt-out handshake option fix(fastify): Add __internal_enableHandshake option to skip handshake flow Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-09-01T17:17:31.387Z

Summary

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

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


@clerk/fastify

Current version: 3.1.74
Recommended bump: MINOR → 3.2.0

Subpath .

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
  type ClerkFastifyOptions = ClerkOptions & {
    hookName?: (typeof ALLOWED_HOOKS)[number];
    frontendApiProxy?: FrontendApiProxyOptions;
+   __internal_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%): A new optional property __internal_enableHandshake? is added to ClerkFastifyOptions, which is an input type (parameter to clerkPlugin); adding an optional property to an input type is non-breaking because existing callers do not need to supply it.

Subpath ./types

🟡 Non-breaking Changes (1)

Modified: ClerkFastifyOptions
  type ClerkFastifyOptions = ClerkOptions & {
    hookName?: (typeof ALLOWED_HOOKS)[number];
    frontendApiProxy?: FrontendApiProxyOptions;
+   __internal_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%): A new optional property __internal_enableHandshake? is added to ClerkFastifyOptions, which is used as an input type (parameter to clerkPlugin); adding an optional input property is non-breaking since existing consumers do not need to pass it.


Report generated by Break Check

Last ran on 3823adc.

… flow

When disabled, the plugin strips handshake cookies and query params before
authenticating and skips handshake redirects, except dev-browser handshakes
that development instances require. A skipped handshake redirect now falls
back to a signed-out auth object instead of a null request.auth, and the
handshake location/cache-control headers are dropped from the reply.

Claude-Session: https://claude.ai/code/session_01KHSkuMqXon3pG5c7ifFTN2
@wobsoriano

Copy link
Copy Markdown
Member

!snapshot

@wobsoriano
wobsoriano enabled auto-merge (squash) September 1, 2026 17:25
@wobsoriano
wobsoriano disabled auto-merge September 1, 2026 17:29
@wobsoriano
wobsoriano merged commit e8f0f9b into main Sep 1, 2026
83 of 84 checks passed
@wobsoriano
wobsoriano deleted the fix/fastify-runtime-secret-handshake branch September 1, 2026 17:30
Sign up for free to 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