') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); feat(clerk-js): Send previous session token on /tokens requests by nikosdouvlis · Pull Request #8105 · clerk/javascript · GitHub
Skip to content

feat(clerk-js): Send previous session token on /tokens requests - #8105

Merged
nikosdouvlis merged 6 commits into
mainfrom
nikos/plat-2566-send-token-on-refresh
Mar 19, 2026
Merged

feat(clerk-js): Send previous session token on /tokens requests#8105
nikosdouvlis merged 6 commits into
mainfrom
nikos/plat-2566-send-token-on-refresh

Conversation

@nikosdouvlis

@nikosdouvlisnikosdouvlis commented Mar 18, 2026

Copy link
Copy Markdown
Member

Why

Session Minter needs the previous session JWT to clone claims at the edge without hitting the DB.

What

Send token (previous session JWT) in the POST body on non-template /tokens requests. Uses conditional spread so the key is absent (not token=) when there's no previous token.

The token param is currently ignored by the backend and will be wired up in PLAT-2471.

Test plan

  • Verify token appears in POST body when a previous token exists
  • Verify token key is absent on first mint
  • Verify token is not sent for template token requests
  • Existing token refresh flows still work

Summary by CodeRabbit

  • New Features

    • Token refresh now sends the previous session token when available to support Session Minter edge token minting.
    • New configuration flag to enable Session Minter behavior.
  • Tests

    • Added tests verifying token refresh requests include the previous token when present, omit it for template-based mints, and preserve sign-in state.

@vercel

vercelBot commented Mar 18, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMar 19, 2026 11:50pm

Request Review

@changeset-bot

changeset-botBot commented Mar 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecdb80a

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

This PR includes changesets to release 3 packages
NameType
@clerk/clerk-jsPatch
@clerk/chrome-extensionPatch
@clerk/expoPatch

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

Send the current session JWT as `token` in the POST body when
requesting a token refresh. This lets the FAPI Proxy forward it to
Session Minter for claim cloning without a DB read.
Uses conditional spread so the key is absent (not `token=`) when
there's no previous token (first mint).
Unit tests verify the token param is present when lastActiveToken
exists, absent on first mint, absent for template requests, and
matches getRawString() exactly. E2e test verifies token refresh
still works with the new param in the POST body.
@coderabbitai

coderabbitaiBot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

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: ASSERTIVE

Plan: Pro

Run ID: 4b284890-bfbb-44ac-a876-53af7f29491e

📥 Commits

Reviewing files that changed from the base of the PR and between 469fb9f and ecdb80a.

📒 Files selected for processing (2)
  • packages/clerk-js/bundlewatch.config.json
  • packages/clerk-js/src/core/resources/__tests__/AuthConfig.test.ts

📝 Walkthrough

Walkthrough

Adds session minter support by sending the previous session token in non-template /tokens requests when enabled. Extends AuthConfigJSON with session_minter?: boolean, adds sessionMinter to AuthConfig and AuthConfigResource, updates Session.#createTokenResolver to include lastActiveToken in the request body when sessionMinter is enabled, and adds unit and integration tests plus a changeset entry for a @clerk/clerk-js patch. Also adjusts a bundle size threshold in bundlewatch.config.json.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'feat(clerk-js): Send previous session token on /tokens requests' accurately summarizes the main change: adding logic to send the previous session token in the request body for /tokens endpoints to support Session Minter edge token minting.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

📝 Coding Plan
  • Generate coding plan for human review comments

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

…nter flag
Only send the previous session JWT in the POST body when the
environment has session_minter enabled. This lets us roll out
Session Minter incrementally via the environment config.
@pkg-pr-new

pkg-pr-newBot commented Mar 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

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

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: ecdb80a

…Config
AuthConfig is the right home for this since it controls auth behavior,
not environment-level display/maintenance settings.
The clerkMock() didn't provide __internal_environment.authConfig.sessionMinter,
so the feature gate in #createTokenResolver was always falsy and token was never
included in the request body, causing 3 test failures.
…imit
Add session_minter to AuthConfig snapshot assertion. Bump clerk.browser.js
bundlewatch limit from 66KB to 67KB to accommodate the new session minter code.
@nikosdouvlis
nikosdouvlis enabled auto-merge (squash) March 19, 2026 23:53
@nikosdouvlis
nikosdouvlis merged commit 2837336 into mainMar 19, 2026
42 of 44 checks passed
@nikosdouvlis
nikosdouvlis deleted the nikos/plat-2566-send-token-on-refresh branch March 19, 2026 23:53
nikosdouvlis added a commit that referenced this pull request May 12, 2026
Resolve tokenCache.ts conflict by adopting main's orphan guard
(cache.get(key) !== value short-circuit from #8098); drop the PR's
redundant compare-and-swap. Monotonic enforcement remains at the
broadcast handler, cookie write path, and Session resource where
user-visible state lives.
Drop stale changesets that were superseded by merged work:
- session-minter-oiat-type.md (oiat field shipped via #8107)
- session-minter-sdk-params.md (previous-token + force_origin shipped
via #8105 + #8106 + #8107)
nikosdouvlis added a commit that referenced this pull request May 13, 2026
Resolve tokenCache.ts conflict by adopting main's orphan guard
(cache.get(key) !== value short-circuit from #8098); drop the PR's
redundant compare-and-swap. Monotonic enforcement remains at the
broadcast handler, cookie write path, and Session resource where
user-visible state lives.
Drop stale changesets that were superseded by merged work:
- session-minter-oiat-type.md (oiat field shipped via #8107)
- session-minter-sdk-params.md (previous-token + force_origin shipped
via #8105 + #8106 + #8107)
Sign up for freeto 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.

2 participants

@nikosdouvlis@bratsos