fix(nextjs): enforce middleware authorization during keyless bootstrap#8369
fix(nextjs): enforce middleware authorization during keyless bootstrap#8369jacekradko wants to merge 6 commits intomainfrom
Conversation
🦋 Changeset detectedLatest commit: 7cce0dc The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
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.
3b24ef7 to
70d5e2b
Compare
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds 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 Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
Summary
Closes SDK-70.
When
clerkMiddlewaredetected no publishable key (the client-side keyless bootstrap window), it early-returnedNextResponse.next()and skipped the user's handler entirely. Any authorization logic inside the handler — includingauth.protect()— was bypassed during that window.The fix runs the user's handler against a synthetic signed-out
RequestStatethrough 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
decorateRequestwith empty keys:encryptClerkRequestDataalready handles this via itsisEmptyguard +KEYLESS_ENCRYPTION_KEYdev fallback.handleControlFlowErrorsredirect with empty pubkey: If the user callsauth.protect()with no configuredsignInUrl,createRedirectthrowsmissingPublishableKeyError— correct fail-closed behavior.baseNextMiddleware, which the no-key branch never enters. Naturally short-circuited.Test plan
@clerk/nextjsbuilds@clerk/backendtests pass (adds coverage forcreateBootstrapSignedOutState)@clerk/nextjstests pass (pre-existingAbortSignalfailures unchanged — unrelated)integration/tests/next-middleware-keyless.test.ts) exercises the bypass end-to-end: boots thenext-app-routertemplate inwithKeylessenv, hits a middleware-protected route, asserts redirect to/sign-ininstead of the protected content rendering.clerkMiddleware((auth, req) => { auth.protect() })on a protected route, no.env.local. Verify the protected route redirects during the keyless bootstrap window.