Skip to content

Commit d4654ee

Browse files
committed
feat(shared,clerk-js): Add better logs for pending session state
Sessions in the `pending` state are treated as signed out by the JS SDK (`isSignedIn` returns false, `useAuth` reports the user signed out) while remaining session tasks are completed. Previously this happened silently, forcing developers to debug "signed in but treated as signed out" with no console hint. iOS and Android SDKs already emit a console message in this state via SessionStatusLogger; this brings the JS SDK to parity. Adds `warnPendingSessionStatus` in @clerk/shared and a single call site in `Clerk.updateClient`, gated on `session?.status === 'pending'`. Dedupes via `logger.warnOnce` keyed by the message string (session id + task key), so a new pending session or a task transition surface a fresh log line. MOBILE-428
1 parent 4696c51 commit d4654ee

4 files changed

Lines changed: 112 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/shared': patch
3+
'@clerk/clerk-js': patch
4+
---
5+
6+
Add a console warning when a session is in the `pending` state. The SDK treats pending sessions as signed out (`isSignedIn` returns `false`, `useAuth` reports signed out) while remaining session tasks are completed, which previously gave no feedback to developers. This brings the JS SDK in line with the equivalent logger on iOS and Android.

packages/clerk-js/src/core/clerk.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
getTaskEndpoint,
3535
navigateIfTaskExists,
3636
warnMissingPendingTaskHandlers,
37+
warnPendingSessionStatus,
3738
} from '@clerk/shared/internal/clerk-js/sessionTasks';
3839
import { warnings } from '@clerk/shared/internal/clerk-js/warnings';
3940
import { windowNavigate } from '@clerk/shared/internal/clerk-js/windowNavigate';
@@ -2892,6 +2893,10 @@ export class Clerk implements ClerkInterface {
28922893
eventBus.emit(events.TokenUpdate, { token: this.session?.lastActiveToken });
28932894
}
28942895

2896+
if (this.session?.status === 'pending') {
2897+
warnPendingSessionStatus(this.session);
2898+
}
2899+
28952900
if (!options?.__internal_dangerouslySkipEmit) {
28962901
this.#emit();
28972902
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2+
3+
import { warnPendingSessionStatus } from '../sessionTasks';
4+
5+
describe('warnPendingSessionStatus', () => {
6+
let warnSpy: ReturnType<typeof vi.spyOn>;
7+
8+
beforeEach(() => {
9+
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
10+
});
11+
12+
afterEach(() => {
13+
warnSpy.mockRestore();
14+
});
15+
16+
it('does not warn when status is active', () => {
17+
warnPendingSessionStatus({ id: 'sess_active', status: 'active', currentTask: undefined as any });
18+
expect(warnSpy).not.toHaveBeenCalled();
19+
});
20+
21+
it('warns when status is pending and includes the session id', () => {
22+
warnPendingSessionStatus({
23+
id: 'sess_pending_1',
24+
status: 'pending',
25+
currentTask: { key: 'choose-organization' },
26+
});
27+
expect(warnSpy).toHaveBeenCalledTimes(1);
28+
expect(warnSpy.mock.calls[0][0]).toContain('sess_pending_1');
29+
expect(warnSpy.mock.calls[0][0]).toContain('pending');
30+
});
31+
32+
it('includes the current task key in the message', () => {
33+
warnPendingSessionStatus({
34+
id: 'sess_pending_2',
35+
status: 'pending',
36+
currentTask: { key: 'choose-organization' },
37+
});
38+
expect(warnSpy.mock.calls[0][0]).toContain('choose-organization');
39+
});
40+
41+
it('omits the tasks suffix when no current task is present', () => {
42+
warnPendingSessionStatus({
43+
id: 'sess_pending_3',
44+
status: 'pending',
45+
currentTask: undefined as any,
46+
});
47+
expect(warnSpy.mock.calls[0][0]).not.toContain('Remaining session tasks');
48+
});
49+
50+
it('dedupes identical messages across calls', () => {
51+
warnPendingSessionStatus({
52+
id: 'sess_pending_dedupe',
53+
status: 'pending',
54+
currentTask: { key: 'choose-organization' },
55+
});
56+
warnPendingSessionStatus({
57+
id: 'sess_pending_dedupe',
58+
status: 'pending',
59+
currentTask: { key: 'choose-organization' },
60+
});
61+
expect(warnSpy).toHaveBeenCalledTimes(1);
62+
});
63+
64+
it('logs again when the task key changes within the same session', () => {
65+
warnPendingSessionStatus({
66+
id: 'sess_pending_task_change',
67+
status: 'pending',
68+
currentTask: { key: 'choose-organization' },
69+
});
70+
warnPendingSessionStatus({
71+
id: 'sess_pending_task_change',
72+
status: 'pending',
73+
currentTask: { key: 'setup-mfa' },
74+
});
75+
expect(warnSpy).toHaveBeenCalledTimes(2);
76+
});
77+
});

packages/shared/src/internal/clerk-js/sessionTasks.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,27 @@ export function warnMissingPendingTaskHandlers(options: Record<string, unknown>)
6969
`Clerk: Session has pending tasks but no handling is configured. To handle pending tasks, provide either "taskUrls" for navigation to custom URLs or "navigate" for programmatic navigation. Without these options, users may get stuck on incomplete flows.`,
7070
);
7171
}
72+
73+
/**
74+
* Warns when a session is in the `pending` state. Mirrors the behavior of
75+
* `SessionStatusLogger` on iOS / Android so developers across platforms get the
76+
* same heads-up that the SDK is treating their signed-in user as signed out
77+
* until the remaining session task is resolved.
78+
*
79+
* Dedupe is per-session/per-task: a new pending session or a task transition
80+
* within the same session both surface a fresh log line.
81+
*/
82+
export function warnPendingSessionStatus(session: Pick<SessionResource, 'id' | 'status' | 'currentTask'>) {
83+
if (session.status !== 'pending') {
84+
return;
85+
}
86+
87+
const taskKey = session.currentTask?.key;
88+
const tasksDescription = taskKey ? ` Remaining session tasks: [${taskKey}].` : '';
89+
90+
logger.warnOnce(
91+
`Clerk: Session ${session.id} is currently pending. ` +
92+
`\`isSignedIn\` will return false and \`useAuth\` will report the user as signed out ` +
93+
`until the remaining session tasks are completed.${tasksDescription}`,
94+
);
95+
}

0 commit comments

Comments
 (0)