feat(js): attach an optional server-configured session token to sign-in - #9299
feat(js): attach an optional server-configured session token to sign-in#9299zourzouvillys wants to merge 4 commits into
Conversation
Mints an opaque, random correlation id and acquires a signed Protect session token once per browser session, shared across tabs under a lock. The token, the correlation id and an acquisition status travel in the form-encoded body of sign-in and sign-up POSTs. Acquisition never blocks a sign-in: on timeout, script-load failure or a non-2xx response a structured status travels in the token's place and the request proceeds. Instances whose loader config does not reference the new placeholders keep today's behaviour and store nothing in the browser.
🦋 Changeset detectedLatest commit: ed13b06 The changes in this PR will be included in the next version bump. This PR includes changesets to release 23 packages
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.
|
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/electron
@clerk/electron-passkeys
@clerk/eslint-plugin
@clerk/expo
@clerk/expo-google-signin
@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: |
Follows the EDR-0020 amendment (clerk/protect#408): the session token now ships inline on the loader's own global rather than behind a second request, so the gate costs no extra round trip on the auth critical path. `tokenUrl` becomes an explicit opt-in for the upgrade mint, and is the only path that can report `fetch_error` or an HTTP status. That removes the derive-the-endpoint-from-an-attribute fallback, which resolved against `document.baseURI` (pointing polling at the app's own origin) and stripped a trailing `{cid}` via relative resolution. The session now owns the token loader: it injects it under the acquisition lock and reads the token when the element fires `load`. Every other loader is applied on every page load, rather than all loaders being suppressed whenever a token happened to be cached. Also fixed: - A malformed loader entry no longer escapes `Protect.load()` and fails `Clerk.load()` for every visitor. - `readStored` falls back to the in-memory store, so a token written there under an exhausted quota is no longer read back as absent. - `getRequestParams` is bounded by the acquisition deadline, and a server-supplied `tokenTimeoutMs` is capped, so a sign-in cannot be stalled before dispatch. It can no longer reject into the request either. - A settled, tokenless acquisition re-arms after a cooldown instead of being replayed for the life of the tab. - The token store and lock are namespaced per instance, so two instances on one origin no longer share a token. - A stored token is validated against a maximum lifetime and length, so a planted entry cannot suppress acquisition indefinitely. - The upgrade mint retries a transient 408/429/5xx within its deadline. - `{instance_id}` alone no longer mints and persists a client id. - `textContent` placeholders are detected and substituted. - `isMergeableBody` admits only plain objects, so a Blob or array body is no longer spread away.
Aligns with clerk/protect: the loader global carries `ready`, a promise
resolving to `{token, exp}` or `{status: "no_token"}`, rather than the
token directly. It is the loader's completion signal and the seam where
page-side probes and the EDR-0020 proof-of-possession challenge will
live, so the token is awaited (raced against the acquisition deadline)
instead of read synchronously.
`{sdkver}` joins the placeholder set, substituted with the build version.
Its presence is what tells the server this build interpolates at all, and
so can be served the current shape; `/v1/environment` is cached and
cannot vary by SDK version, so the negotiation has to happen per request.
A build that leaves it verbatim is served the base `{v, id}` shape.
`no_token` joins the status set, matching Warden's allowlist. It is what
the base shape, a load with no correlation id, and a failed mint all
report. Reporting `timeout` for those would mark every load between this
shipping and the server half deploying as a failure, making a normal
rollout look like an outage.
A `ready` that rejects is treated as nothing served rather than
propagated; the contract says it never rejects, but an unhandled
rejection on every page load is not worth taking on trust.
API Changes Report
Summary
@clerk/sharedCurrent version: 4.25.10 Subpath
|
📝 WalkthroughWalkthroughAdded Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (11)
packages/clerk-js/bundlewatch.config.json (1)
3-7: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winReduce the Bundlewatch ceilings. Set them to
550KB,77KB,119KB,317KB, and77KBfor the five bundles. The current values add unnecessary headroom.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/bundlewatch.config.json` around lines 3 - 7, Update the Bundlewatch maxSize values for the five bundle entries in order: clerk.js to 550KB, clerk.browser.js to 77KB, clerk.legacy.browser.js to 119KB, clerk.no-rhc.js to 317KB, and clerk.native.js to 77KB.packages/shared/src/types/protectConfig.ts (1)
23-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDocument the upper bound for
tokenTimeoutMs.
clampTimeoutinpackages/clerk-js/src/core/protectSession.tslimits this value to 10000 ms and falls back to 5000 for non-positive or non-finite values. The doc comment states only the default. State the ceiling so instance operators know that a larger configured value has no effect.📝 Proposed doc update
/** * How long to wait for the token before giving up and reporting a status instead. Defaults to - * 5000. + * 5000, and is capped at 10000 by the SDK. */ tokenTimeoutMs?: number;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/shared/src/types/protectConfig.ts` around lines 23 - 27, Update the documentation for tokenTimeoutMs in the protect configuration type to state that values are capped at 10000 ms, while retaining the existing 5000 ms default description.packages/clerk-js/src/core/protectSession.ts (2)
419-425: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd an explicit return type to
ProtectSession.create.
createis a public static factory on an exported class. Its return type is inferred asProtectSession | undefined. Declare it so the public surface stays stable if the body changes.🔧 Proposed fix
- static create(loaders: ProtectLoader[], instanceId: string | undefined, applyLoader: ApplyLoader) { + static create( + loaders: ProtectLoader[], + instanceId: string | undefined, + applyLoader: ApplyLoader, + ): ProtectSession | undefined {As per coding guidelines: "Always define explicit return types for functions, especially public APIs". Based on learnings: enforce explicit return type annotations for exported functions and public APIs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/protectSession.ts` around lines 419 - 425, Update the public static factory method ProtectSession.create with an explicit return type annotation of ProtectSession | undefined, preserving its existing undefined result for empty templated loaders and ProtectSession result otherwise.Sources: Coding guidelines, Learnings
5-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReduce the volume of narrative comments.
The file carries many multi-line explanatory blocks. Several restate what the code already shows, for example lines 214-218 and 601-604. Keep the design rationale that is not obvious from the code, such as why
no_tokenis distinct fromtimeout, and shorten the rest to one line each. Consider moving the long module-level narrative to a design document.As per coding guidelines: "Keep code comments minimal. Add comments only when critical to explain why a non-obvious change was made; never restate code behavior, and keep warranted comments to one terse line rather than a verbose multi-line block."
Also applies to: 103-108, 153-156, 214-218, 245-248, 290-299, 506-509, 518-521, 601-604, 687-691
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/protectSession.ts` around lines 5 - 20, Reduce comments throughout protectSession.ts, especially the module-level block and the listed ranges, to terse single-line comments or remove them when they merely restate the surrounding code. Preserve only non-obvious design rationale, including why no_token differs from timeout, and retain concise comments only where they explain that rationale or another critical implementation decision.Source: Coding guidelines
packages/clerk-js/src/core/fapiClient.ts (2)
236-244: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winBound the
getProtectParamsawait insidefapiClient.This
awaitsits on the sign-in and sign-up critical path. The current producer,Protect.getRequestParams, bounds itself with an acquisition deadline of at most 10 s and never rejects, so the request is not blocked indefinitely today.getProtectParamsis a public option onFapiClientOptions, so any other supplier can hang the request forever with no recovery.Add a local deadline so
fapiClientdoes not depend on the caller for that guarantee.🛡️ Proposed defensive bound
+const PROTECT_PARAMS_TIMEOUT_MS = 10_000; + +function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T | undefined> { + return new Promise(resolve => { + const timer = setTimeout(() => resolve(undefined), ms); + const settle = (value: T | undefined) => { + clearTimeout(timer); + resolve(value); + }; + promise.then(settle, () => settle(undefined)); + }); +}- const protectParams = await options.getProtectParams().catch(() => undefined); + const protectParams = await withTimeout(options.getProtectParams(), PROTECT_PARAMS_TIMEOUT_MS);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/fapiClient.ts` around lines 236 - 244, Bound the getProtectParams() await in the fapiClient request flow with a local timeout so a hanging public option cannot block sign-in or sign-up indefinitely. Preserve the existing fallback behavior by treating timeout or supplier rejection as undefined and continuing without protect parameters.
240-243: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the redundant assignment and cast.
isMergeableBodynarrowsbodytoRecord<string, unknown> | undefinedinside this block, so theas Record<string, unknown>cast adds nothing. Line 242 is also redundant, because lines 246-248 reassignrequestInit.bodyfor every plain-object body.♻️ Proposed simplification
const protectParams = await options.getProtectParams().catch(() => undefined); if (protectParams) { - body = { ...((body ?? {}) as Record<string, unknown>), ...protectParams } as unknown as BodyInit; - requestInit.body = body; + body = { ...(body ?? {}), ...protectParams } as unknown as BodyInit; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/fapiClient.ts` around lines 240 - 243, In the protectParams handling near isMergeableBody, remove the redundant Record<string, unknown> cast and the immediate requestInit.body assignment; retain only the merged body assignment, since the later plain-object body flow already updates requestInit.body.packages/clerk-js/src/core/protect.ts (1)
38-41: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winType
#applyinput asunknown[].
config.loaderscomes from the server response and is not validated before this call. The tests passnull,'nope'and42in that array. The parameter typeProtectLoader[]states a guarantee that does not hold, andisLoaderis the guard that establishes it. Declare the input asunknown[]so the narrowing performed byisLoaderis visible in the types.🔧 Proposed change
- `#apply`(configured: ProtectLoader[], instanceId?: string): void { + `#apply`(configured: unknown[], instanceId?: string): void { // Rollout is decided before anything else, because the session is only meaningful for the // loaders we are actually going to apply. const loaders = configured.filter(loader => isLoader(loader) && inRollout(loader));
isLoaderalready returnsloader is ProtectLoader, soloadersstill narrows toProtectLoader[].🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/protect.ts` around lines 38 - 41, Update the `#apply` method parameter from ProtectLoader[] to unknown[] to reflect that server-provided loader values are unvalidated. Preserve the existing configured.filter call so isLoader narrows valid entries to ProtectLoader[] before rollout processing.packages/clerk-js/src/core/__tests__/protectSession.test.ts (3)
370-384: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd a case for a stored token that exceeds
MAX_TOKEN_LENGTH.
validateTokenrejects a token longer than 4096 characters. The suite covers the expiry bound and the lifetime bound, but not the length bound. Add a planted entry with an over-length token and assert thathasFreshToken()returnsfalse.As per coding guidelines: "Verify proper error handling and edge cases".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/__tests__/protectSession.test.ts` around lines 370 - 384, Add a test case alongside the existing planted-token coverage that stores a token longer than MAX_TOKEN_LENGTH in localStorage, then initializes a session and asserts created?.hasFreshToken() is false. Reuse the existing session setup and token-storage key used in protectSession.test.ts, focusing only on the over-length validation path.Source: Coding guidelines
245-255: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSimplify the foreign correlation id.
'z'.repeat(26).replace(/z/g, 'a')produces'a'.repeat(26). Use the direct form so the intent stays readable.♻️ Proposed change
- serveInline(await injected(), { cid: buildCid('z'.repeat(26).replace(/z/g, 'a'), 'b'.repeat(26)) }); + serveInline(await injected(), { cid: buildCid('a'.repeat(26), 'b'.repeat(26)) });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/__tests__/protectSession.test.ts` around lines 245 - 255, In the test case “ignores a token minted for someone else’s run,” simplify the foreign correlation ID passed to buildCid by replacing the redundant 'z'.repeat(26).replace(/z/g, 'a') expression with the direct equivalent 'a'.repeat(26); leave the test behavior unchanged.
298-305: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThese deadline tests depend on real wall-clock time.
tokenTimeoutMsvalues of 40, 50 and 60 ms are shorter than a slow CI tick. A stalled event loop can let the loader path settle after the deadline, or let an unrelated status win. The assertion at line 537 also measures elapsed real time.Raise the deadlines to a value that tolerates scheduler jitter, or drive them with
vi.useFakeTimers(). The test at lines 540-556 already mocksDate.now, so fake timers fit the existing style.Also applies to: 328-334, 531-538
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/__tests__/protectSession.test.ts` around lines 298 - 305, Stabilize the deadline tests in the protectSession suite by replacing the 40, 50, and 60 ms real-time tokenTimeoutMs values and elapsed-time assertion with scheduler-independent timing. Prefer vi.useFakeTimers() while preserving the existing mocked-Date.now style, and update the tests around the timeout cases and the assertion near the elapsed-time check so deadlines are advanced explicitly and timeout remains the winning status.packages/clerk-js/src/core/__tests__/protect.test.ts (1)
172-179: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd coverage for a token loader whose target does not resolve.
applyLoadernow returnsundefinedwhen the#idtarget element is missing, andProtectSession.#runTokenLoadermaps that toscript_error. No test drives that branch. Add a case withtarget: '#missing'on the token loader and assert__clerk_protect_status: 'script_error'.As per coding guidelines: "Verify proper error handling and edge cases".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/__tests__/protect.test.ts` around lines 172 - 179, Add a test alongside the existing loader error case that configures the token loader with target: '`#missing`', invokes the Protect request flow, and asserts it resolves with __clerk_protect_status set to 'script_error'. Exercise the missing-target path in ProtectSession.#runTokenLoader rather than dispatching a script element error.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/lucky-pandas-observe.md:
- Line 12: Update the release note describing ProtectLoader’s tokenUrl field so
it states that the loader token remains inline and tokenUrl is used only for
optional upgrade minting; retain tokenTimeoutMs and the existing optional-field
context.
---
Nitpick comments:
In `@packages/clerk-js/bundlewatch.config.json`:
- Around line 3-7: Update the Bundlewatch maxSize values for the five bundle
entries in order: clerk.js to 550KB, clerk.browser.js to 77KB,
clerk.legacy.browser.js to 119KB, clerk.no-rhc.js to 317KB, and clerk.native.js
to 77KB.
In `@packages/clerk-js/src/core/__tests__/protect.test.ts`:
- Around line 172-179: Add a test alongside the existing loader error case that
configures the token loader with target: '`#missing`', invokes the Protect request
flow, and asserts it resolves with __clerk_protect_status set to 'script_error'.
Exercise the missing-target path in ProtectSession.#runTokenLoader rather than
dispatching a script element error.
In `@packages/clerk-js/src/core/__tests__/protectSession.test.ts`:
- Around line 370-384: Add a test case alongside the existing planted-token
coverage that stores a token longer than MAX_TOKEN_LENGTH in localStorage, then
initializes a session and asserts created?.hasFreshToken() is false. Reuse the
existing session setup and token-storage key used in protectSession.test.ts,
focusing only on the over-length validation path.
- Around line 245-255: In the test case “ignores a token minted for someone
else’s run,” simplify the foreign correlation ID passed to buildCid by replacing
the redundant 'z'.repeat(26).replace(/z/g, 'a') expression with the direct
equivalent 'a'.repeat(26); leave the test behavior unchanged.
- Around line 298-305: Stabilize the deadline tests in the protectSession suite
by replacing the 40, 50, and 60 ms real-time tokenTimeoutMs values and
elapsed-time assertion with scheduler-independent timing. Prefer
vi.useFakeTimers() while preserving the existing mocked-Date.now style, and
update the tests around the timeout cases and the assertion near the
elapsed-time check so deadlines are advanced explicitly and timeout remains the
winning status.
In `@packages/clerk-js/src/core/fapiClient.ts`:
- Around line 236-244: Bound the getProtectParams() await in the fapiClient
request flow with a local timeout so a hanging public option cannot block
sign-in or sign-up indefinitely. Preserve the existing fallback behavior by
treating timeout or supplier rejection as undefined and continuing without
protect parameters.
- Around line 240-243: In the protectParams handling near isMergeableBody,
remove the redundant Record<string, unknown> cast and the immediate
requestInit.body assignment; retain only the merged body assignment, since the
later plain-object body flow already updates requestInit.body.
In `@packages/clerk-js/src/core/protect.ts`:
- Around line 38-41: Update the `#apply` method parameter from ProtectLoader[] to
unknown[] to reflect that server-provided loader values are unvalidated.
Preserve the existing configured.filter call so isLoader narrows valid entries
to ProtectLoader[] before rollout processing.
In `@packages/clerk-js/src/core/protectSession.ts`:
- Around line 419-425: Update the public static factory method
ProtectSession.create with an explicit return type annotation of ProtectSession
| undefined, preserving its existing undefined result for empty templated
loaders and ProtectSession result otherwise.
- Around line 5-20: Reduce comments throughout protectSession.ts, especially the
module-level block and the listed ranges, to terse single-line comments or
remove them when they merely restate the surrounding code. Preserve only
non-obvious design rationale, including why no_token differs from timeout, and
retain concise comments only where they explain that rationale or another
critical implementation decision.
In `@packages/shared/src/types/protectConfig.ts`:
- Around line 23-27: Update the documentation for tokenTimeoutMs in the protect
configuration type to state that values are capped at 10000 ms, while retaining
the existing 5000 ms default description.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 5137fbb9-ce5a-4323-ab93-b45f8ad4be06
📒 Files selected for processing (10)
.changeset/lucky-pandas-observe.mdpackages/clerk-js/bundlewatch.config.jsonpackages/clerk-js/src/core/__tests__/fapiClient.test.tspackages/clerk-js/src/core/__tests__/protect.test.tspackages/clerk-js/src/core/__tests__/protectSession.test.tspackages/clerk-js/src/core/clerk.tspackages/clerk-js/src/core/fapiClient.tspackages/clerk-js/src/core/protect.tspackages/clerk-js/src/core/protectSession.tspackages/shared/src/types/protectConfig.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
clerk/clerk_go(manual)clerk/dashboard(manual)clerk/accounts(manual)clerk/backoffice(manual)clerk/clerk(manual)clerk/clerk-docs(manual)clerk/cloudflare-workers(manual)clerk/clerk-ios(auto-detected)clerk/clerk-android(auto-detected)clerk/cli(auto-detected)
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/clerk-js/src/core/__tests__/protectSession.test.ts (1)
336-384: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winAdd regression coverage for malformed stored token entries. Test invalid JSON, missing or malformed
rid, and non-stringtoken;readStoredTokenmust reject each and start a fresh acquisition without constructing a malformed__clerk_protect_cid.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/clerk-js/src/core/__tests__/protectSession.test.ts` around lines 336 - 384, Extend the protectSession tests around stored-token reuse to cover invalid JSON, missing or malformed rid, and non-string token entries. Verify readStoredToken rejects each case, hasFreshToken() is false, and the session starts fresh acquisition through the loader without producing a malformed __clerk_protect_cid; reuse the existing session, loader, and request-parameter setup.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/clerk-js/src/core/__tests__/protectSession.test.ts`:
- Around line 336-384: Extend the protectSession tests around stored-token reuse
to cover invalid JSON, missing or malformed rid, and non-string token entries.
Verify readStoredToken rejects each case, hasFreshToken() is false, and the
session starts fresh acquisition through the loader without producing a
malformed __clerk_protect_cid; reuse the existing session, loader, and
request-parameter setup.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: 681154a7-1772-4ec0-9732-fd243673889d
📒 Files selected for processing (10)
.changeset/lucky-pandas-observe.mdpackages/clerk-js/bundlewatch.config.jsonpackages/clerk-js/src/core/__tests__/fapiClient.test.tspackages/clerk-js/src/core/__tests__/protect.test.tspackages/clerk-js/src/core/__tests__/protectSession.test.tspackages/clerk-js/src/core/clerk.tspackages/clerk-js/src/core/fapiClient.tspackages/clerk-js/src/core/protect.tspackages/clerk-js/src/core/protectSession.tspackages/shared/src/types/protectConfig.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
clerk/clerk_go(manual)clerk/dashboard(manual)clerk/accounts(manual)clerk/backoffice(manual)clerk/clerk(manual)clerk/clerk-docs(manual)clerk/cloudflare-workers(manual)clerk/clerk-ios(auto-detected)clerk/clerk-android(auto-detected)clerk/cli(auto-detected)
🚧 Files skipped from review as they are similar to previous changes (8)
- packages/clerk-js/src/core/clerk.ts
- packages/clerk-js/src/core/fapiClient.ts
- packages/clerk-js/bundlewatch.config.json
- .changeset/lucky-pandas-observe.md
- packages/shared/src/types/protectConfig.ts
- packages/clerk-js/src/core/tests/fapiClient.test.ts
- packages/clerk-js/src/core/protect.ts
- packages/clerk-js/src/core/protectSession.ts
Description
Adds an optional, server-configured session token to
@clerk/clerk-js, attached to sign-in and sign-up requests.It is inert unless an instance's loader configuration opts into it. An instance not using it keeps today's behaviour exactly, stores nothing in the browser, and sends no additional parameters.
{cid},{pid},{rid},{instance_id},{sdkver}— substituted into a loader's attribute values andtextContentbefore the element is appended. An unrecognised{…}is left verbatim, so an older SDK loading the same configuration stays compatible.crypto.getRandomValuesas lowercase unpadded RFC 4648 base32. Nothing is derived from the device — no fingerprinting input, no clock, no user data.SafeLockprimitive (core/auth/safeLock.ts) thatSessionCookiePolleralready uses, with a re-check inside the lock so concurrent tabs do not each acquire in turn. A wedged leader delays nobody, and a leader tab closing mid-run releases its lock automatically.Clerk.load()rather than at sign-in, so acquisition stays off the latency-critical path. It is bounded by a deadline and can never block or fail a sign-in: when no token is obtained, a status travels in its place and the request proceeds unchanged._methodinto the query. No request header is added anywhere. The merge is an explicit step infapiClient.requeston a path allowlist, because theonBeforeRequestcallbacks fire after the body is stringified.ProtectLoadergains two optional fields,tokenUrlandtokenTimeoutMs, both inert unless set.rolloutmoved out ofapplyLoaderinto a module-level helper soload()can decide participation before it starts applying loaders. Same semantics, evaluated once.Bundle thresholds were bumped on the affected bundles to cover the addition.
Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change