Skip to content

fix(nextjs): enforce middleware authorization during keyless bootstrap#8369

Open
jacekradko wants to merge 6 commits intomainfrom
jacek/sdk-70-keyless-fix
Open

fix(nextjs): enforce middleware authorization during keyless bootstrap#8369
jacekradko wants to merge 6 commits intomainfrom
jacek/sdk-70-keyless-fix

Conversation

@jacekradko
Copy link
Copy Markdown
Member

@jacekradko jacekradko commented Apr 21, 2026

Summary

Closes SDK-70.

When clerkMiddleware detected no publishable key (the client-side keyless bootstrap window), it early-returned NextResponse.next() and skipped the user's handler entirely. Any authorization logic inside the handler — including auth.protect() — was bypassed during that window.

The fix runs the user's handler against a synthetic signed-out RequestState through the same post-authentication pipeline (runHandlerWithRequestState) that the keyed path uses. Authorization fails closed during bootstrap. <ClerkProvider/> downstream resumes the flow once keys are provisioned client-side. Dev-only path — production behavior unchanged.

Edge cases — verified, no code changes needed

  • decorateRequest with empty keys: encryptClerkRequestData already handles this via its isEmpty guard + KEYLESS_ENCRYPTION_KEY dev fallback.
  • handleControlFlowErrors redirect with empty pubkey: If the user calls auth.protect() with no configured signInUrl, createRedirect throws missingPublishableKeyError — correct fail-closed behavior.
  • Frontend API proxy: The proxy check lives in baseNextMiddleware, which the no-key branch never enters. Naturally short-circuited.

Test plan

  • @clerk/nextjs builds
  • @clerk/backend tests pass (adds coverage for createBootstrapSignedOutState)
  • @clerk/nextjs tests pass (pre-existing AbortSignal failures unchanged — unrelated)
  • New Playwright integration test (integration/tests/next-middleware-keyless.test.ts) exercises the bypass end-to-end: boots the next-app-router template in withKeyless env, hits a middleware-protected route, asserts redirect to /sign-in instead of the protected content rendering.
  • Manual smoke: fresh Next.js app with clerkMiddleware((auth, req) => { auth.protect() }) on a protected route, no .env.local. Verify the protected route redirects during the keyless bootstrap window.
  • Manual smoke: public route during keyless bootstrap still renders and still triggers key provisioning.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 21, 2026

🦋 Changeset detected

Latest commit: 7cce0dc

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

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

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

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

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 21, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
clerk-js-sandbox Skipped Skipped Apr 22, 2026 8:05pm

Request Review

Base automatically changed from jacek/sdk-70-factor-middleware to main April 22, 2026 16:33
Previously, `clerkMiddleware` early-returned `NextResponse.next()` when it
detected no publishable key (the client-side keyless bootstrap window), which
skipped the user's handler entirely. Any authorization logic inside that
handler — including `auth.protect()` — was bypassed during the bootstrap window.

The fix runs the user's handler against a synthetic signed-out RequestState
(via the new `createBootstrapSignedOutState` helper in @clerk/backend/internal)
through the same post-authentication pipeline as the normal path. Authorization
fails closed during bootstrap; `<ClerkProvider/>` downstream resumes the flow
once keys are provisioned client-side. Dev-only path — production behavior is
unchanged.

Closes SDK-70.
@jacekradko jacekradko force-pushed the jacek/sdk-70-keyless-fix branch from 3b24ef7 to 70d5e2b Compare April 22, 2026 17:17
@jacekradko jacekradko marked this pull request as ready for review April 22, 2026 18:53
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: 745c7641-68f9-40fc-b237-461649c9baae

📥 Commits

Reviewing files that changed from the base of the PR and between c187b2b and 7cce0dc.

📒 Files selected for processing (1)
  • packages/nextjs/src/server/clerkMiddleware.ts

📝 Walkthrough

Walkthrough

Adds a keyless bootstrap path to Next.js middleware: when the publishable key is absent and the request is not a machine token, middleware now constructs an empty-key options object (empty publishableKey/secretKey with resolved sign-in/sign-up URLs), persists it to the middleware data store, creates a Clerk request, generates a synthetic signed-out RequestState via createBootstrapSignedOutState, and runs the normal handler/authorization flow using runHandlerWithRequestState. Replaces the prior early NextResponse.next() pass-through for keyless requests. Also adds unit tests for the bootstrap signed-out state and a Playwright integration test for keyless middleware behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: enforcing middleware authorization during the keyless bootstrap window, which is the core fix described throughout the PR.
Description check ✅ Passed The description is directly related to the changeset, providing a detailed explanation of the bug fix, implementation approach, edge cases, and test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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


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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 22, 2026

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@8369

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 7cce0dc

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.

1 participant