Parameters
| Param | Default | Notes |
|---|---|---|
enabled | true | false purges every SSR cookie |
header | none | Header name to inject on main-frame navigations. Authorization gets a Bearer prefix, any other name gets the raw JWT |
domains | app host + localhost + 127.0.0.1 | Comma-separated override of the cookie/host scope |
Cookies
Three cookies, scoped per configured domain, refreshed every 50 seconds.| Cookie | Value | Used by |
|---|---|---|
__session | Session JWT | Clerk’s clerkMiddleware() |
__client_uat | Unix-seconds signed in, 0 signed out | Clerk’s middleware signed-in/out signal |
clerk_token | Session JWT | Manual @clerk/backend verification or client-side reads |
HttpOnly (so client code on localhost can read clerk_token), Secure on real hosts, and not Secure on localhost / 127.0.0.1 so they work over http://localhost:port.
Header transport
Withheader=, the bridge intercepts main-frame GETs to configured domains and attaches the header.
Authorization gets a Bearer prefix. Any other header (X-Clerk-JWT, X-Auth) gets the raw JWT.
Next.js
clerkMiddleware() reads __session and __client_uat from request cookies, so the standard middleware just works.
<ClerkProvider>, no useAuth() hook, no web SDK. The native bridge writes the cookies and the middleware does the rest.
Manual verification
For any other backend, read the JWT and verify it with@clerk/backend.
sub. Never accept a client-reported userId.
Handshake invariant
Clerk’s middleware treats “a live__client_uat next to a missing/expired __session” as a redirect to Clerk’s FAPI for a handshake, which a native WebView with no Clerk web SDK cannot satisfy. The bridge guarantees the cookie pair is only ever in one of two states middleware accepts without a handshake:
- Signed-in: fresh JWT in
__sessionplus__client_uat > 0 - Signed-out: no
__sessionplus__client_uat = 0
Cold launch
The first page load of a cold launch is anonymous, Clerk is not configured until your page’s JS has run and calledclerk://configure. That first load lands in the safe signed-out state, never a handshake. Every navigation after configure is authenticated.
If you need authenticated SSR on the very first byte, configure earlier in your boot or render a brief loading shell that calls configure and then navigates to the real route once window.clerkJWT is populated.
Offline
A failed refresh leaves the existing cookies in place. Once they hit their 55-second TTL the browser presents the signed-out state to middleware rather than the handshake combination. The user effectively becomes signed-out for new server-rendered requests until the device comes back online.Resources
NPM Package
despia-native