From 035ae209c0430a7b2a77468b407321eaf9a58fa9 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 22 Sep 2026 13:25:58 -0700 Subject: [PATCH 1/7] fix(clerk-js): preserve indefinite session deadlines Preserve zero timestamps through date conversion and snapshots, and expose hasMaximumLifetime on session resources. Part of MOBILE-517 --- .changeset/native-session-lifetime.md | 6 +++++ .../clerk-js/src/core/resources/Session.ts | 4 ++++ .../core/resources/SessionWithActivities.ts | 4 ++++ .../core/resources/__tests__/Session.test.ts | 23 +++++++++++++++++++ .../clerk-js/src/utils/__tests__/date.test.ts | 4 ++++ packages/clerk-js/src/utils/date.ts | 2 +- packages/shared/src/types/session.ts | 6 +++-- 7 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 .changeset/native-session-lifetime.md diff --git a/.changeset/native-session-lifetime.md b/.changeset/native-session-lifetime.md new file mode 100644 index 00000000000..b1e8a9c18c8 --- /dev/null +++ b/.changeset/native-session-lifetime.md @@ -0,0 +1,6 @@ +--- +"@clerk/clerk-js": patch +"@clerk/shared": patch +--- + +Preserve indefinite native session deadlines when loading and restoring sessions. Sessions expose `hasMaximumLifetime` to distinguish a configured expiration from an indefinite lifetime. diff --git a/packages/clerk-js/src/core/resources/Session.ts b/packages/clerk-js/src/core/resources/Session.ts index 981a30a6f6c..af6ca0a91c8 100644 --- a/packages/clerk-js/src/core/resources/Session.ts +++ b/packages/clerk-js/src/core/resources/Session.ts @@ -87,6 +87,10 @@ export class Session extends BaseResource implements SessionResource { return !!resource && resource instanceof Session; } + get hasMaximumLifetime(): boolean { + return this.expireAt.getTime() > 0; + } + constructor(data: SessionJSON | SessionJSONSnapshot) { super(); diff --git a/packages/clerk-js/src/core/resources/SessionWithActivities.ts b/packages/clerk-js/src/core/resources/SessionWithActivities.ts index 23c8ababed2..62bbfed7ac6 100644 --- a/packages/clerk-js/src/core/resources/SessionWithActivities.ts +++ b/packages/clerk-js/src/core/resources/SessionWithActivities.ts @@ -30,6 +30,10 @@ export class SessionWithActivities extends BaseResource implements SessionWithAc latestActivity!: SessionActivity; actor!: ActClaim | null; + get hasMaximumLifetime(): boolean { + return this.expireAt.getTime() > 0; + } + constructor(data: SessionWithActivitiesJSON, pathRoot: string) { super(); this.pathRoot = pathRoot; diff --git a/packages/clerk-js/src/core/resources/__tests__/Session.test.ts b/packages/clerk-js/src/core/resources/__tests__/Session.test.ts index 33ce91597e1..428a59c3d10 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Session.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Session.test.ts @@ -16,6 +16,7 @@ import { eventBus } from '../../events'; import { createFapiClient } from '../../fapiClient'; import { SessionTokenCache } from '../../tokenCache'; import { BaseResource, Client, Organization, Session } from '../internal'; +import { SessionWithActivities } from '../SessionWithActivities'; const baseFapiClientOptions = { frontendApi: 'clerk.example.com', @@ -40,6 +41,28 @@ describe('Session', () => { vi.useRealTimers(); }); + it.each([0, 2000000000000])('preserves a session deadline of %s through snapshots', deadline => { + const session = new Session({ + id: 'session_lifetime', + status: 'active', + expire_at: deadline, + abandon_at: deadline, + public_user_data: { user_id: 'user_1' }, + } as SessionJSON); + expect(session.expireAt.getTime()).toBe(deadline); + expect(session.abandonAt.getTime()).toBe(deadline); + expect(session.hasMaximumLifetime).toBe(deadline > 0); + const restored = new Session(session.__internal_toSnapshot()); + expect(restored.expireAt.getTime()).toBe(deadline); + expect(restored.hasMaximumLifetime).toBe(deadline > 0); + const withActivities = new SessionWithActivities( + { id: session.id, expire_at: deadline, abandon_at: deadline } as any, + '/me/sessions', + ); + expect(withActivities.hasMaximumLifetime).toBe(deadline > 0); + expect(withActivities.abandonAt.getTime()).toBe(deadline); + }); + describe('getToken()', () => { let dispatchSpy; diff --git a/packages/clerk-js/src/utils/__tests__/date.test.ts b/packages/clerk-js/src/utils/__tests__/date.test.ts index 3c1ff3a3ca7..7394ca2fe0a 100644 --- a/packages/clerk-js/src/utils/__tests__/date.test.ts +++ b/packages/clerk-js/src/utils/__tests__/date.test.ts @@ -13,6 +13,10 @@ describe('date utilities', () => { }); describe('unixEpochToDate', () => { + it('preserves an unset session deadline', () => { + expect(unixEpochToDate(0).getTime()).toBe(0); + }); + it('converts a unix epoch to Date', function () { const epoch = 1622575087; const date = unixEpochToDate(epoch); diff --git a/packages/clerk-js/src/utils/date.ts b/packages/clerk-js/src/utils/date.ts index 9541ca70bab..eafa94d1c8f 100644 --- a/packages/clerk-js/src/utils/date.ts +++ b/packages/clerk-js/src/utils/date.ts @@ -3,6 +3,6 @@ function isValidDate(d: Date | unknown) { } export function unixEpochToDate(epochInSeconds?: number): Date { - const date = new Date(epochInSeconds || new Date()); + const date = new Date(epochInSeconds ?? new Date()); return isValidDate(date) ? date : new Date(); } diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index 878fd6e8ecb..aa6b1e1a11d 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -219,11 +219,12 @@ export interface SessionResource extends ClerkResource { */ status: SessionStatus; /** - * The date and time when the session will expire. + * The date and time when the session will expire. Unix epoch zero means no maximum lifetime. */ expireAt: Date; + readonly hasMaximumLifetime?: boolean; /** - * The date and time when the session was abandoned by the user. + * The abandonment deadline. Unix epoch zero means no abandonment deadline. */ abandonAt: Date; /** @@ -378,6 +379,7 @@ export interface SessionWithActivitiesResource extends ClerkResource { id: string; status: string; expireAt: Date; + readonly hasMaximumLifetime?: boolean; abandonAt: Date; lastActiveAt: Date; latestActivity: SessionActivity; From 62e3c1719a9d62c15e2b8c0f39572894318ffb08 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 13 Oct 2026 21:53:16 -0700 Subject: [PATCH 2/7] test(clerk-js): cover restoring an indefinite session from a client snapshot Part of MOBILE-517 --- .../core/resources/__tests__/Client.test.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/clerk-js/src/core/resources/__tests__/Client.test.ts b/packages/clerk-js/src/core/resources/__tests__/Client.test.ts index 5514e1cc855..34261720ebf 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Client.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Client.test.ts @@ -193,6 +193,32 @@ describe('Client Singleton', () => { expect(client.signIn.status).toBe('needs_second_factor'); }); + it('restores a session without a maximum lifetime from a client snapshot', () => { + const user = createUser({ first_name: 'John', last_name: 'Doe', id: 'user_1' }); + const clientJSON: ClientJSON = { + object: 'client', + id: 'test_id', + status: 'active', + last_active_session_id: 'session_1', + sign_in: null, + sign_up: null, + sessions: [{ ...createSession({ id: 'session_1', status: 'active' }, user), expire_at: 0, abandon_at: 0 }], + created_at: Date.now() - 1000, + updated_at: Date.now(), + } as any; + + // @ts-expect-error We cannot mess with the singleton when tests are running in parallel + const snapshot = new Client(clientJSON).__internal_toSnapshot(); + // @ts-expect-error We cannot mess with the singleton when tests are running in parallel + const [restored] = new Client(snapshot).sessions; + + expect(snapshot.sessions[0]).toMatchObject({ expire_at: 0, abandon_at: 0 }); + expect(restored.expireAt.getTime()).toBe(0); + expect(restored.abandonAt.getTime()).toBe(0); + expect(restored.hasMaximumLifetime).toBe(false); + expect(restored.status).toBe('active'); + }); + it('replaces sign up and sign in identity when fromJSON receives new ids', () => { const user = createUser({ first_name: 'John', last_name: 'Doe', id: 'user_1' }); const session = createSession({ id: 'session_1' }, user); From 18db78f01c50667348482d19b6eea7b41943cd1f Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 13 Oct 2026 21:53:17 -0700 Subject: [PATCH 3/7] docs(shared): document hasMaximumLifetime and mark the change as minor hasMaximumLifetime is a new public property, so clerk-js and shared take a minor bump. The changeset now describes the user-facing fix. Part of MOBILE-517 --- .changeset/native-session-lifetime.md | 6 +++--- packages/shared/src/types/session.ts | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.changeset/native-session-lifetime.md b/.changeset/native-session-lifetime.md index b1e8a9c18c8..4124979ac4e 100644 --- a/.changeset/native-session-lifetime.md +++ b/.changeset/native-session-lifetime.md @@ -1,6 +1,6 @@ --- -"@clerk/clerk-js": patch -"@clerk/shared": patch +"@clerk/clerk-js": minor +"@clerk/shared": minor --- -Preserve indefinite native session deadlines when loading and restoring sessions. Sessions expose `hasMaximumLifetime` to distinguish a configured expiration from an indefinite lifetime. +Fix `expireAt` and `abandonAt` being replaced with the current time for sessions without a maximum lifetime, which the Frontend API sends as `0`. Both now stay at Unix epoch zero, including when a cached client is restored. Add `hasMaximumLifetime` to `SessionResource` and `SessionWithActivitiesResource`; it is `false` when the session has no maximum lifetime. diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index c04ebd9356a..d37719cc20f 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -224,6 +224,9 @@ export interface SessionResource extends ClerkResource { * The date and time when the session will expire. Unix epoch zero means no maximum lifetime. */ expireAt: Date; + /** + * Whether the session has a maximum lifetime. `false` when `expireAt` is Unix epoch zero. A session without a maximum lifetime can still end through sign-out or revocation, so use `status` to check whether it is active. `undefined` in ClerkJS versions that predate this property. + */ readonly hasMaximumLifetime?: boolean; /** * The abandonment deadline. Unix epoch zero means no abandonment deadline. @@ -381,6 +384,9 @@ export interface SessionWithActivitiesResource extends ClerkResource { id: string; status: string; expireAt: Date; + /** + * Whether the session has a maximum lifetime. `false` when `expireAt` is Unix epoch zero. `undefined` in ClerkJS versions that predate this property. + */ readonly hasMaximumLifetime?: boolean; abandonAt: Date; lastActiveAt: Date; From baa8e61626fa1109eee6470bd9d619707c9bba2a Mon Sep 17 00:00:00 2001 From: Robert Soriano Date: Thu, 15 Oct 2026 14:23:46 -0700 Subject: [PATCH 4/7] chore(repo): tighten native session lifetime changeset --- .changeset/native-session-lifetime.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/native-session-lifetime.md b/.changeset/native-session-lifetime.md index 4124979ac4e..df5ee01a35b 100644 --- a/.changeset/native-session-lifetime.md +++ b/.changeset/native-session-lifetime.md @@ -1,6 +1,6 @@ --- -"@clerk/clerk-js": minor -"@clerk/shared": minor +'@clerk/clerk-js': minor +'@clerk/shared': minor --- -Fix `expireAt` and `abandonAt` being replaced with the current time for sessions without a maximum lifetime, which the Frontend API sends as `0`. Both now stay at Unix epoch zero, including when a cached client is restored. Add `hasMaximumLifetime` to `SessionResource` and `SessionWithActivitiesResource`; it is `false` when the session has no maximum lifetime. +Add `hasMaximumLifetime` to `SessionResource` and `SessionWithActivitiesResource`. It is `false` for sessions without a maximum lifetime, whose `expireAt` and `abandonAt` now stay at Unix epoch zero instead of being replaced with the current time. From 1538f120303acabbde97981b9ad4dc4f98588567 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 15 Oct 2026 18:15:04 -0600 Subject: [PATCH 5/7] docs(session): explain indefinite native session fields --- packages/backend/src/api/resources/Session.ts | 8 ++++++-- packages/shared/src/types/session.ts | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/api/resources/Session.ts b/packages/backend/src/api/resources/Session.ts index 968be156748..cb52f230154 100644 --- a/packages/backend/src/api/resources/Session.ts +++ b/packages/backend/src/api/resources/Session.ts @@ -52,9 +52,13 @@ export class Session { readonly status: string, /** The time the session was last active on the [`Client`](https://clerk.com/docs/reference/backend/types/backend-client). */ readonly lastActiveAt: number, - /** The Unix timestamp when the `Session` will expire. */ + /** + * The Unix timestamp when the `Session` will expire. For an [indefinite native session](https://clerk.com/docs/guides/secure/session-options#native-sessions), this is `0`, meaning no expiration deadline. Check `status` to determine whether the session is active. + */ readonly expireAt: number, - /** The Unix timestamp when the `Session` will be abandoned. */ + /** + * The Unix timestamp when the `Session` will be abandoned. For an [indefinite native session](https://clerk.com/docs/guides/secure/session-options#native-sessions), this is `0`, meaning no abandonment deadline. + */ readonly abandonAt: number, /** The Unix timestamp when the `Session` was first created. */ readonly createdAt: number, diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index d37719cc20f..f65c1191a1d 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -221,7 +221,7 @@ export interface SessionResource extends ClerkResource { */ status: SessionStatus; /** - * The date and time when the session will expire. Unix epoch zero means no maximum lifetime. + * The date and time when the session will expire. For an [indefinite native session](https://clerk.com/docs/guides/secure/session-options#native-sessions), this represents Unix epoch zero, meaning no expiration deadline is configured. */ expireAt: Date; /** @@ -229,7 +229,7 @@ export interface SessionResource extends ClerkResource { */ readonly hasMaximumLifetime?: boolean; /** - * The abandonment deadline. Unix epoch zero means no abandonment deadline. + * The abandonment deadline. For an [indefinite native session](https://clerk.com/docs/guides/secure/session-options#native-sessions), this represents Unix epoch zero, meaning no abandonment deadline is configured. */ abandonAt: Date; /** From 7b3a07d0f8dbd9969357ab4656a6e9e9c18743c1 Mon Sep 17 00:00:00 2001 From: Sarah Soutoul Date: Thu, 15 Oct 2026 18:19:40 -0600 Subject: [PATCH 6/7] docs(session): omit unpublished native section links --- packages/backend/src/api/resources/Session.ts | 4 ++-- packages/shared/src/types/session.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/api/resources/Session.ts b/packages/backend/src/api/resources/Session.ts index cb52f230154..41f32d54f7b 100644 --- a/packages/backend/src/api/resources/Session.ts +++ b/packages/backend/src/api/resources/Session.ts @@ -53,11 +53,11 @@ export class Session { /** The time the session was last active on the [`Client`](https://clerk.com/docs/reference/backend/types/backend-client). */ readonly lastActiveAt: number, /** - * The Unix timestamp when the `Session` will expire. For an [indefinite native session](https://clerk.com/docs/guides/secure/session-options#native-sessions), this is `0`, meaning no expiration deadline. Check `status` to determine whether the session is active. + * The Unix timestamp when the `Session` will expire. For an indefinite native session, this is `0`, meaning no expiration deadline. Check `status` to determine whether the session is active. */ readonly expireAt: number, /** - * The Unix timestamp when the `Session` will be abandoned. For an [indefinite native session](https://clerk.com/docs/guides/secure/session-options#native-sessions), this is `0`, meaning no abandonment deadline. + * The Unix timestamp when the `Session` will be abandoned. For an indefinite native session, this is `0`, meaning no abandonment deadline. */ readonly abandonAt: number, /** The Unix timestamp when the `Session` was first created. */ diff --git a/packages/shared/src/types/session.ts b/packages/shared/src/types/session.ts index f65c1191a1d..7eaaae5b11c 100644 --- a/packages/shared/src/types/session.ts +++ b/packages/shared/src/types/session.ts @@ -221,7 +221,7 @@ export interface SessionResource extends ClerkResource { */ status: SessionStatus; /** - * The date and time when the session will expire. For an [indefinite native session](https://clerk.com/docs/guides/secure/session-options#native-sessions), this represents Unix epoch zero, meaning no expiration deadline is configured. + * The date and time when the session will expire. For an indefinite native session, this represents Unix epoch zero, meaning no expiration deadline is configured. */ expireAt: Date; /** @@ -229,7 +229,7 @@ export interface SessionResource extends ClerkResource { */ readonly hasMaximumLifetime?: boolean; /** - * The abandonment deadline. For an [indefinite native session](https://clerk.com/docs/guides/secure/session-options#native-sessions), this represents Unix epoch zero, meaning no abandonment deadline is configured. + * The abandonment deadline. For an indefinite native session, this represents Unix epoch zero, meaning no abandonment deadline is configured. */ abandonAt: Date; /** From e886110f098cb25e35319b687d54a62dc2a8325e Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 21 Oct 2026 17:30:58 -0700 Subject: [PATCH 7/7] fix(clerk-js): limit zero deadline handling to session resources Other resources keep falling back to the current time for a zero timestamp, as before. Session and SessionWithActivities parse expire_at and abandon_at with unixEpochToDeadline, which keeps zero as Unix epoch zero. Part of MOBILE-517 --- .../clerk-js/src/core/resources/Session.ts | 6 +++--- .../core/resources/SessionWithActivities.ts | 6 +++--- .../core/resources/__tests__/Session.test.ts | 17 ++++++++++++++++ .../clerk-js/src/utils/__tests__/date.test.ts | 20 ++++++++++++++++--- packages/clerk-js/src/utils/date.ts | 6 +++++- 5 files changed, 45 insertions(+), 10 deletions(-) diff --git a/packages/clerk-js/src/core/resources/Session.ts b/packages/clerk-js/src/core/resources/Session.ts index 516d99b2bd2..fcdec0d1468 100644 --- a/packages/clerk-js/src/core/resources/Session.ts +++ b/packages/clerk-js/src/core/resources/Session.ts @@ -44,7 +44,7 @@ import type { } from '@clerk/shared/types'; import { isWebAuthnSupported as isWebAuthnSupportedOnWindow } from '@clerk/shared/webauthn'; -import { unixEpochToDate } from '@/utils/date'; +import { unixEpochToDate, unixEpochToDeadline } from '@/utils/date'; import { debugLogger } from '@/utils/debug'; import { getTabState, isTabFocused } from '@/utils/isTabFocused'; import { TokenId } from '@/utils/tokenId'; @@ -442,8 +442,8 @@ export class Session extends BaseResource implements SessionResource { this.id = data.id; this.status = data.status; - this.expireAt = unixEpochToDate(data.expire_at); - this.abandonAt = unixEpochToDate(data.abandon_at); + this.expireAt = unixEpochToDeadline(data.expire_at); + this.abandonAt = unixEpochToDeadline(data.abandon_at); this.factorVerificationAge = data.factor_verification_age; this.lastActiveAt = unixEpochToDate(data.last_active_at || undefined); this.lastActiveOrganizationId = data.last_active_organization_id; diff --git a/packages/clerk-js/src/core/resources/SessionWithActivities.ts b/packages/clerk-js/src/core/resources/SessionWithActivities.ts index 62bbfed7ac6..23ef01de808 100644 --- a/packages/clerk-js/src/core/resources/SessionWithActivities.ts +++ b/packages/clerk-js/src/core/resources/SessionWithActivities.ts @@ -6,7 +6,7 @@ import type { SessionWithActivitiesResource, } from '@clerk/shared/types'; -import { unixEpochToDate } from '../../utils/date'; +import { unixEpochToDate, unixEpochToDeadline } from '../../utils/date'; import { BaseResource } from './internal'; const mapSessionActivityJSONToSessionActivity = (data: SessionActivityJSON): SessionActivity => ({ @@ -69,8 +69,8 @@ export class SessionWithActivities extends BaseResource implements SessionWithAc this.id = data.id; this.status = data.status; - this.expireAt = unixEpochToDate(data.expire_at); - this.abandonAt = unixEpochToDate(data.abandon_at); + this.expireAt = unixEpochToDeadline(data.expire_at); + this.abandonAt = unixEpochToDeadline(data.abandon_at); this.lastActiveAt = unixEpochToDate(data.last_active_at || undefined); this.latestActivity = mapSessionActivityJSONToSessionActivity(data.latest_activity ?? ({} as SessionActivityJSON)); this.actor = data.actor; diff --git a/packages/clerk-js/src/core/resources/__tests__/Session.test.ts b/packages/clerk-js/src/core/resources/__tests__/Session.test.ts index 5c80cae052e..b34c06543c6 100644 --- a/packages/clerk-js/src/core/resources/__tests__/Session.test.ts +++ b/packages/clerk-js/src/core/resources/__tests__/Session.test.ts @@ -63,6 +63,23 @@ describe('Session', () => { expect(withActivities.abandonAt.getTime()).toBe(deadline); }); + it('keeps zero only for session deadlines', () => { + const session = new Session({ + id: 'session_lifetime', + status: 'active', + expire_at: 0, + abandon_at: 0, + created_at: 0, + updated_at: 0, + public_user_data: { user_id: 'user_1' }, + } as SessionJSON); + + expect(session.expireAt.getTime()).toBe(0); + expect(session.abandonAt.getTime()).toBe(0); + expect(session.createdAt.getTime()).toBe(Date.now()); + expect(session.updatedAt.getTime()).toBe(Date.now()); + }); + describe('getToken()', () => { let dispatchSpy; diff --git a/packages/clerk-js/src/utils/__tests__/date.test.ts b/packages/clerk-js/src/utils/__tests__/date.test.ts index 7394ca2fe0a..0b8a303999e 100644 --- a/packages/clerk-js/src/utils/__tests__/date.test.ts +++ b/packages/clerk-js/src/utils/__tests__/date.test.ts @@ -1,6 +1,6 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; -import { unixEpochToDate } from '../date'; +import { unixEpochToDate, unixEpochToDeadline } from '../date'; describe('date utilities', () => { beforeAll(() => { @@ -13,8 +13,8 @@ describe('date utilities', () => { }); describe('unixEpochToDate', () => { - it('preserves an unset session deadline', () => { - expect(unixEpochToDate(0).getTime()).toBe(0); + it('fallbacks to current date for zero', function () { + expect(unixEpochToDate(0).getTime()).toBe(Date.now()); }); it('converts a unix epoch to Date', function () { @@ -33,4 +33,18 @@ describe('date utilities', () => { expect(date.getTime()).toBe(Date.now()); }); }); + + describe('unixEpochToDeadline', () => { + it('keeps zero as Unix epoch zero', () => { + expect(unixEpochToDeadline(0).getTime()).toBe(0); + }); + + it('converts a unix epoch to Date', () => { + expect(unixEpochToDeadline(1622575087).getTime()).toBe(1622575087); + }); + + it('fallbacks to current date for undefined values', () => { + expect(unixEpochToDeadline(undefined).getTime()).toBe(Date.now()); + }); + }); }); diff --git a/packages/clerk-js/src/utils/date.ts b/packages/clerk-js/src/utils/date.ts index eafa94d1c8f..f785cbe118e 100644 --- a/packages/clerk-js/src/utils/date.ts +++ b/packages/clerk-js/src/utils/date.ts @@ -3,6 +3,10 @@ function isValidDate(d: Date | unknown) { } export function unixEpochToDate(epochInSeconds?: number): Date { - const date = new Date(epochInSeconds ?? new Date()); + const date = new Date(epochInSeconds || new Date()); return isValidDate(date) ? date : new Date(); } + +export function unixEpochToDeadline(epoch?: number): Date { + return epoch === 0 ? new Date(0) : unixEpochToDate(epoch); +}