Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/remove-zero-session-deadlines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/clerk-js': patch
'@clerk/shared': patch
'@clerk/backend': patch
---

Session `expireAt` and `abandonAt` no longer special-case a `0` deadline; the backend now always sends a real deadline.
8 changes: 2 additions & 6 deletions packages/backend/src/api/resources/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,9 @@ 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. For an indefinite native session, this is `0`, meaning no expiration deadline. Check `status` to determine whether the session is active.
*/
/** The Unix timestamp when the `Session` will expire. */
readonly expireAt: number,
/**
* The Unix timestamp when the `Session` will be abandoned. For an indefinite native session, this is `0`, meaning no abandonment deadline.
*/
/** The Unix timestamp when the `Session` will be abandoned. */
readonly abandonAt: number,
/** The Unix timestamp when the `Session` was first created. */
readonly createdAt: number,
Expand Down
6 changes: 3 additions & 3 deletions packages/clerk-js/src/core/resources/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import type {
} from '@clerk/shared/types';
import { isWebAuthnSupported as isWebAuthnSupportedOnWindow } from '@clerk/shared/webauthn';

import { unixEpochToDate, unixEpochToDeadline } from '@/utils/date';
import { unixEpochToDate } from '@/utils/date';
import { debugLogger } from '@/utils/debug';
import { getTabState, isTabFocused } from '@/utils/isTabFocused';
import { TokenId } from '@/utils/tokenId';
Expand Down Expand Up @@ -438,8 +438,8 @@ export class Session extends BaseResource implements SessionResource {

this.id = data.id;
this.status = data.status;
this.expireAt = unixEpochToDeadline(data.expire_at);
this.abandonAt = unixEpochToDeadline(data.abandon_at);
this.expireAt = unixEpochToDate(data.expire_at);
this.abandonAt = unixEpochToDate(data.abandon_at);
this.factorVerificationAge = data.factor_verification_age;
this.lastActiveAt = unixEpochToDate(data.last_active_at || undefined);
this.lastActiveOrganizationId = data.last_active_organization_id;
Expand Down
6 changes: 3 additions & 3 deletions packages/clerk-js/src/core/resources/SessionWithActivities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
SessionWithActivitiesResource,
} from '@clerk/shared/types';

import { unixEpochToDate, unixEpochToDeadline } from '../../utils/date';
import { unixEpochToDate } from '../../utils/date';
import { BaseResource } from './internal';

const mapSessionActivityJSONToSessionActivity = (data: SessionActivityJSON): SessionActivity => ({
Expand Down Expand Up @@ -65,8 +65,8 @@ export class SessionWithActivities extends BaseResource implements SessionWithAc

this.id = data.id;
this.status = data.status;
this.expireAt = unixEpochToDeadline(data.expire_at);
this.abandonAt = unixEpochToDeadline(data.abandon_at);
this.expireAt = unixEpochToDate(data.expire_at);
this.abandonAt = unixEpochToDate(data.abandon_at);
this.lastActiveAt = unixEpochToDate(data.last_active_at || undefined);
this.latestActivity = mapSessionActivityJSONToSessionActivity(data.latest_activity ?? ({} as SessionActivityJSON));
this.actor = data.actor;
Expand Down
25 changes: 0 additions & 25 deletions packages/clerk-js/src/core/resources/__tests__/Client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,31 +193,6 @@ 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.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);
Expand Down
39 changes: 0 additions & 39 deletions packages/clerk-js/src/core/resources/__tests__/Session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ 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',
Expand All @@ -41,44 +40,6 @@ 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);
const restored = new Session(session.__internal_toSnapshot());
expect(restored.expireAt.getTime()).toBe(deadline);
expect(restored.abandonAt.getTime()).toBe(deadline);
const withActivities = new SessionWithActivities(
{ id: session.id, expire_at: deadline, abandon_at: deadline } as any,
'/me/sessions',
);
expect(withActivities.expireAt.getTime()).toBe(deadline);
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;

Expand Down
20 changes: 1 addition & 19 deletions packages/clerk-js/src/utils/__tests__/date.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';

import { unixEpochToDate, unixEpochToDeadline } from '../date';
import { unixEpochToDate } from '../date';

describe('date utilities', () => {
beforeAll(() => {
Expand All @@ -13,10 +13,6 @@ describe('date utilities', () => {
});

describe('unixEpochToDate', () => {
it('fallbacks to current date for zero', function () {
expect(unixEpochToDate(0).getTime()).toBe(Date.now());
});

it('converts a unix epoch to Date', function () {
const epoch = 1622575087;
const date = unixEpochToDate(epoch);
Expand All @@ -33,18 +29,4 @@ 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());
});
});
});
4 changes: 0 additions & 4 deletions packages/clerk-js/src/utils/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,3 @@ export function unixEpochToDate(epochInSeconds?: number): 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);
}
4 changes: 2 additions & 2 deletions packages/shared/src/types/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ export interface SessionResource extends ClerkResource {
*/
status: SessionStatus;
/**
* 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.
* The date and time when the session will expire.
*/
expireAt: Date;
/**
* The abandonment deadline. For an indefinite native session, this represents Unix epoch zero, meaning no abandonment deadline is configured.
* The date and time when the session will be abandoned due to inactivity.
*/
abandonAt: Date;
/**
Expand Down
Loading