javascript icon indicating copy to clipboard operation
javascript copied to clipboard

fix(backend): Resolve Safari ITP handshake loop

Open jacekradko opened this issue 2 months ago • 4 comments

Description

Looks like Safari ITP sometimes blocks the Strict cookies on cross-origin redirects ( __clerk_uat ) in development so if you are redirecting directly to a protected route, the middleware will keep trying to handshake in order to acquire that particular cookie leading to an infinite redirect loop.

In the scenario where we detect a handshake redirect loop (attempt 2+), we just validate the token and return signed in instead of forcing a handshake.

Checklist

  • [ ] pnpm test runs as expected.
  • [ ] pnpm build runs as expected.
  • [ ] (If applicable) JSDoc comments have been added or updated for any package exports
  • [ ] (If applicable) Documentation has been updated

Type of change

  • [ ] 🐛 Bug fix
  • [ ] 🌟 New feature
  • [ ] 🔨 Breaking change
  • [ ] 📖 Refactoring / dependency upgrade / documentation
  • [ ] other:

Summary by CodeRabbit

  • Bug Fixes

    • Prevents infinite handshake redirect loops in browsers that block cross-origin cookies (e.g., Safari ITP) by verifying existing session tokens so valid sessions remain signed in.
  • Tests

    • Added tests covering redirect-loop scenarios to ensure correct handling of valid and invalid session tokens.
  • Chores

    • Added a patch changeset documenting the redirect-loop fix for the backend package.

✏️ Tip: You can customize this high-level summary in your review settings.

jacekradko avatar Dec 01 '25 19:12 jacekradko

🦋 Changeset detected

Latest commit: fe817e4f7ca0c091e12d2f3513e1f925bb947a0d

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

This PR includes changesets to release 11 packages
Name Type
@clerk/backend Patch
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/remix Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch

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

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

changeset-bot[bot] avatar Dec 01 '25 19:12 changeset-bot[bot]

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

Project Deployment Preview Comments Updated (UTC)
clerk-js-sandbox Ready Ready Preview Comment Dec 4, 2025 2:33pm

vercel[bot] avatar Dec 01 '25 19:12 vercel[bot]

Walkthrough

Adds redirect-loop detection to authenticateRequest: when no clientUat exists but a session token and a redirect-count cookie are present, it tries to verify the session token and returns a signed-in state on success; on failure it falls back to the existing handshake flow.

Changes

Cohort / File(s) Summary
Redirect-loop handling & tests
packages/backend/src/tokens/request.ts, packages/backend/src/tokens/__tests__/request.test.ts
Adds a conditional branch in authenticateRequest to detect redirect loops via __clerk_redirect_count; if no clientUat but a session token exists it attempts cookie-based session verification and returns a signed-in auth state on success, otherwise falls back to handshake (SessionTokenWithoutClientUAT). Adds two tests covering successful verification and handshake fallback.
Changeset / Manifest
.changeset/true-carpets-happen.md, package.json
Adds a patch changeset describing the redirect-loop fix for development browsers that block cross-origin Strict cookies and updates package metadata as part of the release changeset.

Sequence Diagram

sequenceDiagram
    participant Client
    participant AuthRequest as authenticateRequest()
    participant CookieStore
    participant SessionVerifier

    Client->>AuthRequest: HTTP request (cookies include __clerk_redirect_count)
    AuthRequest->>CookieStore: Read session token & redirect count

    alt No clientUat && session token present && redirect_count > 0
        AuthRequest->>SessionVerifier: Verify session token from cookie
        alt Verification succeeds
            SessionVerifier-->>AuthRequest: Valid session token & claims
            AuthRequest-->>Client: Return signed-in auth state (session token)
        else Verification fails
            SessionVerifier-->>AuthRequest: Invalid or expired token
            AuthRequest-->>Client: Continue to handshake (SessionTokenWithoutClientUAT / null auth)
        end
    else Normal flow
        AuthRequest-->>Client: Proceed with existing auth/handshake logic
    end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Inspect packages/backend/src/tokens/request.ts for correctness of the new conditional, cookie parsing, and error/edge-case handling.
  • Verify test accuracy and JWKS mocking in packages/backend/src/tokens/__tests__/request.test.ts.
  • Check .changeset/true-carpets-happen.md and package.json for correct changeset metadata and versioning intent.

Poem

🐰
A loop that chased us round and round,
I sniffed the cookie, then found the sound.
If token sings, I'll open the gate—
If silent, handshake decides our fate.
Hops saved, code tidy, rabbit elate!

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: resolving an infinite handshake redirect loop caused by Safari ITP blocking cross-origin redirect cookies.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment
  • [ ] Commit unit tests in branch fix/safari-itp-handshake-loop

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

coderabbitai[bot] avatar Dec 01 '25 19:12 coderabbitai[bot]

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@7335
@clerk/astro

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

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

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

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

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

npm i https://pkg.pr.new/@clerk/elements@7335
@clerk/clerk-expo

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

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

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

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

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

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

npm i https://pkg.pr.new/@clerk/nuxt@7335
@clerk/clerk-react

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

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

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

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

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

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

npm i https://pkg.pr.new/@clerk/themes@7335
@clerk/types

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

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

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

commit: fe817e4

pkg-pr-new[bot] avatar Dec 01 '25 19:12 pkg-pr-new[bot]