Skip to content

feat(js): send tab state with session token requests#9215

Open
nikosdouvlis wants to merge 3 commits into
mainfrom
nikos/tokens-page-state
Open

feat(js): send tab state with session token requests#9215
nikosdouvlis wants to merge 3 commits into
mainfrom
nikos/tokens-page-state

Conversation

@nikosdouvlis

@nikosdouvlis nikosdouvlis commented Jul 22, 2026

Copy link
Copy Markdown
Member

Why
Session activity counts pretty much every /tokens call as user activity, but backgrounded tabs keep refreshing tokens on a timer, so idle sessions look active. There's nothing on the wire to tell a foreground mint from a background one. On top of that, since the proactive refresh landed in #7317 the tab that last minted re-arms its own refresh timer, so refresh duty sticks to one tab regardless of focus. A hidden tab can hold it indefinitely while the user works elsewhere, which would skew the new signal in multi-tab apps.

What changed
Non-template session token requests now include a tab_state body param with one of focused, visible or hidden, derived at request time: document.hasFocus() wins, then visibilityState, and the param is omitted when document doesn't exist (service workers, headless runtimes). Template tokens and touch are untouched.

Refresh ownership is also biased toward the focused tab: network mints attach the proactive refresh callback only when the tab is focused, and the session poller ticks at 1.5s while focused (5s otherwise). An unfocused tab still mints when its turn comes, it just doesn't keep the job, so within a cycle or two the refreshes (and the tab_state they report) come from the tab the user is actually in. Browsers with no focused tab fall back to the regular 5s poller floor, and runtimes without a usable document behave exactly as before.

Blockers / related
Merging is safe but releasing isn't yet: FAPI rejects unknown form params with a 422, so this must not go out in a clerk-js release before clerk/clerk_go #20618 is deployed. The Session Minter passes the param through untouched, no changes needed there.

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jul 23, 2026 1:46pm
swingset Ready Ready Preview, Comment Jul 23, 2026 1:46pm

Request Review

@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 090f789

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

This PR includes changesets to release 4 packages
Name Type
@clerk/clerk-js Patch
@clerk/chrome-extension Patch
@clerk/electron Patch
@clerk/expo 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

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-23T13:47:52.196Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 1
🔴 Breaking changes 1
🟡 Non-breaking changes 1
🟢 Additions 0

Warning
1 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (1)

Every breaking change, up front. Full diffs are in the package sections below.

Package Subpath Change
@clerk/ui ./styles.css ./styles.css

@clerk/ui

Version: 1.25.7 → 1.25.6
Recommended bump: MAJOR

Subpath ./styles.css

🔴 Breaking Changes (1)

Changed: ./styles.css

Subpath export ./styles.css was removed

Subpath ./internal

🟡 Non-breaking Changes (1)

Modified: ElementsConfig
// ... 31 unchanged lines elided ...
    logoImage: WithOptions;
    logoGroup: WithOptions;
    logoGroupItem: WithOptions;
-   logoGroupItemContainer: WithOptions;
    logoGroupIcon: WithOptions;
    logoGroupSeparator: WithOptions;
    listGroup: WithOptions;
// ... 518 unchanged lines elided ...

Static analyzer: Breaking change in type alias ElementsConfig: Type changed: {button:import("@clerk/ui").~WithOptions<string>;input:import("@clerk/ui").~WithOptions;checkbox:import("@clerk/ui").~W…{button:import("@clerk/ui").~WithOptions<string>;input:import("@clerk/ui").~WithOptions;checkbox:import("@clerk/ui").~W…

🤖 AI review (reclassified as non-breaking) (85%): The diff shows new keys added to ElementsConfig (e.g., listGroupContent, logoGroupItemContainer removed, minor renames) and logoGroupItemContainer removed — but ElementsConfig is used only as a mapped-type input to produce Elements (an output type), and the keys of ElementsConfig become CSS selector keys. Adding new keys to this object type only expands the set of available selectors; removing logoGroupItemContainer from before and adding listGroupContent in after is a net addition of a new optional selector key. Consumers who read Elements get more variants; no existing code constructing values of ElementsConfig directly is broken because it's an internal mapped-type source, not a consumer-constructed type.


Report generated by Break Check

Last ran on 090f789.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Session token requests now include tab_state values derived from browser focus and visibility when available. Background refresh ownership is limited for unfocused tabs, and focused tabs use a shorter session-cookie polling interval. Tests cover browser-state variations and failure cases, with changesets documenting the patch.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: sending tab state with session token requests.
Description check ✅ Passed The description is clearly related to the changeset and matches the tab-state and focused-refresh behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9215

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9215

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9215

@clerk/expo

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

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9215

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: 08d2c3c

Since the proactive refresh landed, the tab that last minted re-arms its own
refresh timer, so refresh duty sticks to one tab regardless of focus and a
hidden tab can hold it indefinitely, under-reporting focused activity. Attach
the refresh timer only in focused tabs and poll at 1.5s while focused (5s
otherwise) so duty migrates to the tab the user is actually in.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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 `@packages/clerk-js/src/core/resources/Session.ts`:
- Around line 242-252: Re-check isTabFocused() !== false when each scheduled
refresh executes, not only when callbacks are created. Guard the hydrated-token
callback at packages/clerk-js/src/core/resources/Session.ts:242-252, the
network-minted token refresh at
packages/clerk-js/src/core/resources/Session.ts:593-598, and subsequent
refresh-cycle execution at
packages/clerk-js/src/core/resources/Session.ts:664-669, preferably at
`#refreshTokenInBackground` or each callback entry point; add a
focused-to-unfocused transition test and ensure refresh responsibility follows
the currently focused tab.
🪄 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: da7805cb-17d8-4b2f-81d3-a100cc606762

📥 Commits

Reviewing files that changed from the base of the PR and between 816773d and 08d2c3c.

📒 Files selected for processing (6)
  • .changeset/focused-refresh-bias.md
  • packages/clerk-js/src/core/auth/SessionCookiePoller.ts
  • packages/clerk-js/src/core/auth/__tests__/SessionCookiePoller.test.ts
  • packages/clerk-js/src/core/resources/Session.ts
  • packages/clerk-js/src/core/resources/__tests__/Session.test.ts
  • packages/clerk-js/src/utils/isTabFocused.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go (manual) → reviewed against open PR #20618 nikos/tokens-tab-state instead of the default branch
  • clerk/dashboard (manual)
  • clerk/accounts (manual)
  • clerk/backoffice (manual)
  • clerk/clerk (manual)
  • clerk/clerk-docs (manual)
  • clerk/cloudflare-workers (manual)

Comment thread packages/clerk-js/src/core/resources/Session.ts Outdated
Post-review cleanup, no behavior change: getTabState moves next to
isTabFocused so the document guard exists once, the three conditional
onRefresh spreads collapse into a focusedRefresh helper, and the three
copies of the document descriptor stub/restore machinery in tests become
one shared helper that also covers visibilityState.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/clerk-js/src/core/resources/Session.ts (1)

493-498: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Forward tab_state through FAPI before releasing this client change.

The linked clerk/clerk_go branch accepts the form parameter but drops it: CreateSessionTokenParams and its downstream token-mint call have no tab-state field. This client will send the value, but the service cannot consume it. Add and forward the field server-side before rollout.

🤖 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/resources/Session.ts` around lines 493 - 498,
Update the server-side CreateSessionTokenParams flow to accept the tab_state
form parameter and add a corresponding tab-state field to the downstream
token-mint request. Forward the received value through the create-session-token
handler into the token-mint call, preserving existing behavior when it is
absent, before releasing the client change.

Source: Linked repositories

🤖 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.

Outside diff comments:
In `@packages/clerk-js/src/core/resources/Session.ts`:
- Around line 493-498: Update the server-side CreateSessionTokenParams flow to
accept the tab_state form parameter and add a corresponding tab-state field to
the downstream token-mint request. Forward the received value through the
create-session-token handler into the token-mint call, preserving existing
behavior when it is absent, before releasing the client change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: c81db20c-b5aa-4330-821e-da32771a5bc2

📥 Commits

Reviewing files that changed from the base of the PR and between 08d2c3c and 090f789.

📒 Files selected for processing (5)
  • packages/clerk-js/src/core/auth/__tests__/SessionCookiePoller.test.ts
  • packages/clerk-js/src/core/resources/Session.ts
  • packages/clerk-js/src/core/resources/__tests__/Session.test.ts
  • packages/clerk-js/src/test/document-helpers.ts
  • packages/clerk-js/src/utils/isTabFocused.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go (manual) → reviewed against open PR #20618 nikos/tokens-tab-state instead of the default branch
  • clerk/dashboard (manual)
  • clerk/accounts (manual)
  • clerk/backoffice (manual)
  • clerk/clerk (manual)
  • clerk/clerk-docs (manual)
  • clerk/cloudflare-workers (manual)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/clerk-js/src/core/auth/tests/SessionCookiePoller.test.ts
  • packages/clerk-js/src/core/resources/tests/Session.test.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants