Skip to content

fix(clerk-js): Preserve indefinite session deadlines (Core 2) - #10269

Open
swolfand wants to merge 6 commits into
release/core-2from
sam/core-2-zero-session-deadlines
Open

swolfand wants to merge 6 commits into
release/core-2from
sam/core-2-zero-session-deadlines

Conversation

@swolfand

@swolfand swolfand commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Description

Backport of #9871 to Core 2 (ClerkJS 5.x).

Native sessions can have no maximum lifetime (MOBILE-517). FAPI sends a missing deadline as expire_at: 0 / abandon_at: 0. On Core 2, unixEpochToDate treats 0 as falsy and substitutes the current time. A session without a maximum lifetime therefore loaded with expireAt and abandonAt set to "now". __internal_toSnapshot() then wrote that time back out, so a restored client carried a fabricated finite deadline.

This change keeps Unix epoch zero for session deadlines only, using the same helper as Core 3:

  • unixEpochToDeadline in utils/date.ts returns new Date(0) for 0 and otherwise defers to unixEpochToDate. Its name, signature, body and tests match Core 3 main, so later fixes cherry-pick cleanly.
  • Session and SessionWithActivities use it for expire_at and abandon_at. Snapshots already serialize getTime(), so zero now round-trips.
  • unixEpochToDate is unchanged, so every other date field still falls back to the current time for 0, as before.

There is no hasMaximumLifetime property; it was dropped from both Core 2 and Core 3. This is a patch fix for @clerk/clerk-js. It adds no public API.

unixEpochToDate(epochInSeconds) has always received millisecond timestamps from every caller, despite its parameter name. This PR deliberately leaves that name alone.

Release this before enabling the backend native_session_lifetimes policy (clerk/clerk_go#22369) for instances that may run Core 2 SDKs. The rollout contract requires compatible SDKs before the flag is turned on.

Part of MOBILE-517. Core 3 fix: #9871 and #10364. Backend: clerk/clerk_go#22459, clerk/clerk_go#22460, clerk/clerk_go#22461, clerk/clerk_go#22462, clerk/clerk_go#22369.

Checklist

  • pnpm test runs as expected. @clerk/shared, @clerk/clerk-js, and @clerk/clerk-expo unit tests passed; full monorepo suite not run.
  • pnpm build runs as expected. @clerk/shared, @clerk/clerk-js, @clerk/clerk-expo, and @clerk/chrome-extension built; full monorepo build not run.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

End to end on a Core 2 Expo app

I ran a Core 2 app against a local clerk_go stack that sends real zero deadlines: sam/mobile-session-lifetimes, the top of the native-lifetime stack, run with make up/demo plus the Platform API. I created a development instance through PLAPI, turned on native_session_lifetimes for it, and saved native_maximum_lifetime: {"enabled": false, "duration_seconds": 0} through PATCH …/config. A native password sign-in (_is_native=1) to that instance returns expire_at: 0 and abandon_at: 0.

The app is a blank Expo SDK 58 app running in Expo Go on an iOS 26 simulator (iPhone 17 Pro). It uses @clerk/clerk-expo@2.20.2 with tokenCache and __experimental_resourceCache, so the client snapshot is saved to SecureStore and restored when FAPI can't be reached. The screen shows the session's expireAt/abandonAt and the raw expire_at/abandon_at from the cached snapshot. Only @clerk/clerk-js differs between the runs:

  • Before: @clerk/clerk-js@5.128.1 from npm.
  • After: @clerk/clerk-js packed from this branch. Its build matches head c393468f08 at runtime; the one source difference is a deleted comment.

Each run signs in, then relaunches the app with the local nginx (FAPI) stopped. That relaunch makes ClerkJS restore the cached snapshot (clerk.status degraded).

State Before (5.128.1 from npm) After (this PR)
Signed in (from FAPI) Before: expireAt and abandonAt show the sign-in time, and the cached snapshot stores that time After: expireAt and abandonAt are 0, and the cached snapshot stores 0
Relaunched offline (from cache) Before: restored session shows a past deadline equal to the earlier sign-in time After: restored session keeps expireAt and abandonAt at 0
  • Before: ClerkJS replaces the zero deadlines with the moment the client was read (19:01:48Z) and writes that time into the cached snapshot. After the offline relaunch, the session has no maximum lifetime but reports a deadline that has already passed.
  • After: both deadlines stay at Unix epoch zero in memory, in the SecureStore snapshot, and after the restore. Session status is active in every state, and ClerkJS loads normally from FAPI.

The app, the local instance and the test user (+clerk_test email, generated password) exist only on my machine. Nothing ran against a hosted Clerk instance.

Backport of #9871. Keep expire_at and abandon_at of 0 as Unix epoch zero for Session and SessionWithActivities instead of substituting the current time, so sessions without a maximum lifetime survive loading and snapshot restoration. The shared unixEpochToDate helper is unchanged for non-session callers.
@vercel

vercel Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
clerk-js-sandbox Ready Ready Preview Oct 22, 2026 10:28pm UTC
swingset Error Error Oct 22, 2026 10:28pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3863df0

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

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

@coderabbitai

coderabbitai Bot commented Oct 15, 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

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
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

…rces

Session and SessionWithActivities expose hasMaximumLifetime, false when expireAt is Unix epoch zero. The SessionResource and SessionWithActivitiesResource types gain the optional property with the same documentation as Core 3. Bump clerk-js, shared and types as minor, and drop the comment above unixEpochToSessionDeadline.

Part of MOBILE-517
@pkg-pr-new

pkg-pr-new Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@clerk/agent-toolkit

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

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/dev-cli

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

@clerk/elements

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

@clerk/clerk-expo

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/clerk-react

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

@clerk/react-router

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

@clerk/remix

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/themes

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

@clerk/types

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

@clerk/upgrade

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

@clerk/vue

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

commit: 3863df0

…ources

Remove hasMaximumLifetime from Session, SessionWithActivities and their resource types. Session expireAt and abandonAt still stay at Unix epoch zero for sessions without a maximum lifetime, and the changeset is now a clerk-js patch for that fix.

Part of MOBILE-517

@dmoerner dmoerner 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.

A few nits:

  1. Let's bring this as close as possible to the Core 3 PR: Use the same helper name unixEpochToDeadline so we can cherry-pick fixes easily if necessary. I think the description is also stale, it looks like the last commit on #9871 changed it to use the same helper design.
  2. With this helper we are now supposedly passing milliseconds into a helper that takes seconds. However, I think the helper always took milliseconds and the name was incorrect. We shouldn't change this in the code now but we can document it in the PR description.
  3. As we discussed I think we should just test this E2E with a Core 2 app, Claude can do it.
  4. Just a reminder that I think we need to deploy this to the infra workers to hotload it before we ship. Last time we had to ship a Core 2 fix there was actually some sort of bottleneck figuring out who even had permissions to do it, but I think Bryce probably does and he was just OOO then.

Rename unixEpochToSessionDeadline to unixEpochToDeadline and use the same helper, tests and session fallback test as Core 3 main, so later fixes cherry-pick cleanly.

Part of MOBILE-517

This branch had an error being deployed

1 failed and 1 active deployments
Preview – clerk-js-sandbox — 3863df0e Deployed Oct 22, 2026 by vercel[bot]
Preview – swingset — 3863df0e Deployed Oct 22, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants