') + ')', '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,shared): Backport Session Minter SDK changes to core-2 by nikosdouvlis · Pull Request #8643 · clerk/javascript · GitHub
Skip to content

feat(clerk-js,shared): Backport Session Minter SDK changes to core-2 - #8643

Merged
nikosdouvlis merged 7 commits into
release/core-2from
nikos/port-minter-to-core-2
May 27, 2026
Merged

feat(clerk-js,shared): Backport Session Minter SDK changes to core-2#8643
nikosdouvlis merged 7 commits into
release/core-2from
nikos/port-minter-to-core-2

Conversation

@nikosdouvlis

Copy link
Copy Markdown
Member

Why

Session Minter (edge token minting) shipped to clerk-js on main between March and May 2026 across five small PRs. Until now, only the touch-intent change (#8135) was backported to release/core-2. The other five SDK changes have to land on core-2 too so v5 consumers can opt into edge-minted tokens once their instance flag is flipped on.

This is a behavior-preserving backport: every code path is gated behind auth_config.session_minter, which defaults to false. With the gate off, clerk-js v5 sends the same /tokens request body and runs the same retry path it does today.

What changed

Five upstream PRs ported, one per commit:

  • #8107 - add optional oiat field to JwtHeader
  • #8097 - monotonic cross-tab token replacement via pickFreshestJwt, so a stale edge-minted broadcast cannot clobber a fresher cached token
  • #8105 - send the previous session token in the /tokens POST body when auth_config.session_minter is true
  • #8108 - skip the missing_expired_token retry when Session Minter is enabled (the token is now in the body, so the retry-with-expired-token fallback is unnecessary). Legacy retry preserved when the flag is off.
  • #8106 - send forceOrigin=true in the body when skipCache is true, so FAPI routes to origin instead of the Session Minter edge

A sixth commit cleans up a review-pass finding: rather than introduce a new __internal_environment getter on Clerk (which exists on main but not on core-2), the code now reads the existing __unstable__environment getter that has been on core-2 since v3. Same commit also backports the Session test coverage from #8105 and #8106 that wasn't worth porting until the env-getter question was settled.

Packages affected

  • @clerk/clerk-js: AuthConfig gains a sessionMinter field, Session sends the new body fields when the flag is on, tokenCache compares broadcast tokens with pickFreshestJwt. All behavior is gated.
  • @clerk/shared: AuthConfigResource.sessionMinter, AuthConfigJSON.session_minter?, JwtHeader.oiat?. All additive.

Test plan

  • pnpm vitest run src/core in clerk-js: 380/380 pass
  • pnpm test in shared: 641/641 pass
  • New unit tests in tokenFreshness.test.ts cover the broadcast-side guard's edge cases (legacy tokens, equal oiat, tie-break)
  • New Session.test.ts blocks assert the /tokens body shape for both sessionMinter: true and sessionMinter: false
  • Manual: smoke test in a v5 host app against a staging instance with the flag on

@vercel

vercelBot commented May 26, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentMay 26, 2026 2:06pm

Request Review

@coderabbitai

coderabbitaiBot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ae76f5f0-3dce-48f5-b972-30695445e273

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch nikos/port-minter-to-core-2

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

@pkg-pr-new

pkg-pr-newBot commented May 26, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

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

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/elements

npm i https://pkg.pr.new/@clerk/elements@8643

@clerk/clerk-expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/clerk-react

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

@clerk/react-router

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

@clerk/remix

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/themes

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

@clerk/types

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

@clerk/upgrade

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

@clerk/vue

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

commit: ccff619

@nikosdouvlis

Copy link
Copy Markdown
MemberAuthor

The two failing checks (Integration Tests (machine, chrome) and Integration Tests (machine, chrome, RQ)) are pre-existing on release/core-2, not introduced by this PR.

#8633, the most recently merged PR against release/core-2 (a pure CI version-bump with no code changes), has the same two failures. The earlier PRs #8321, #8310, #8239 all had these checks passing, so the M2M test regressed somewhere between then and now.

The failure is m2m.test.ts:139 "authorizes M2M requests when sender machine has proper access to receiver machine" - it sends an M2M bearer token to a protected endpoint and expects 200, but gets 401. M2M tokens are a separate auth flow from user session tokens; nothing in this port touches the M2M code path.

Static analysis (including bundlewatch) is now green after I bumped the clerk.headless*.js limit from 67 KB to 68 KB.

@nikosdouvlis
nikosdouvlis merged commit 9d421ef into release/core-2May 27, 2026
72 of 77 checks passed
@nikosdouvlis
nikosdouvlis deleted the nikos/port-minter-to-core-2 branch May 27, 2026 06:58
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.

1 participant

@nikosdouvlis