Skip to content

Commit e040597

Browse files
authored
chore(clerk-js): Trigger Next.js hooks on session status transition to pending (#6511)
1 parent fd21c42 commit e040597

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

.changeset/mean-jobs-stare.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/clerk-js': patch
3+
---
4+
5+
Trigger Next.js hooks on session status transition from `active` to `pending` to update authentication context state

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2543,4 +2543,68 @@ describe('Clerk singleton', () => {
25432543
});
25442544
});
25452545
});
2546+
2547+
describe('updateClient', () => {
2548+
afterEach(() => {
2549+
// cleanup global window pollution
2550+
(window as any).__unstable__onBeforeSetActive = null;
2551+
(window as any).__unstable__onAfterSetActive = null;
2552+
});
2553+
2554+
it('runs server revalidation hooks when session transitions from `active` to `pending`', async () => {
2555+
const mockOnBeforeSetActive = jest.fn().mockReturnValue(Promise.resolve());
2556+
const mockOnAfterSetActive = jest.fn().mockReturnValue(Promise.resolve());
2557+
(window as any).__unstable__onBeforeSetActive = mockOnBeforeSetActive;
2558+
(window as any).__unstable__onAfterSetActive = mockOnAfterSetActive;
2559+
2560+
const mockActiveSession = {
2561+
id: 'session_1',
2562+
status: 'active',
2563+
user: { id: 'user_1' },
2564+
lastActiveToken: { getRawString: () => 'token_1' },
2565+
};
2566+
2567+
const mockPendingSession = {
2568+
id: 'session_1',
2569+
status: 'pending',
2570+
user: { id: 'user_1' },
2571+
lastActiveToken: { getRawString: () => 'token_1' },
2572+
};
2573+
2574+
const mockInitialClient = {
2575+
sessions: [mockActiveSession],
2576+
signedInSessions: [mockActiveSession],
2577+
lastActiveSessionId: 'session_1',
2578+
};
2579+
2580+
const mockUpdatedClient = {
2581+
sessions: [mockPendingSession],
2582+
signedInSessions: [mockPendingSession],
2583+
lastActiveSessionId: 'session_1',
2584+
};
2585+
2586+
const sut = new Clerk(productionPublishableKey);
2587+
2588+
// Manually set the initial client and session state to simulate active session
2589+
// without going through load() or setActive()
2590+
sut.updateClient(mockInitialClient as any);
2591+
2592+
// Verify we start with an active session
2593+
expect(sut.session?.status).toBe('active');
2594+
2595+
// Call updateClient with the new client that has pending session
2596+
sut.updateClient(mockUpdatedClient as any);
2597+
2598+
// Verify hooks were called
2599+
await waitFor(() => {
2600+
expect(mockOnBeforeSetActive).toHaveBeenCalledTimes(1);
2601+
expect(mockOnAfterSetActive).toHaveBeenCalledTimes(1);
2602+
});
2603+
2604+
// Verify that onAfterSetActive was called after onBeforeSetActive
2605+
const beforeCallTime = mockOnBeforeSetActive.mock.invocationCallOrder[0];
2606+
const afterCallTime = mockOnAfterSetActive.mock.invocationCallOrder[0];
2607+
expect(afterCallTime).toBeGreaterThan(beforeCallTime);
2608+
});
2609+
});
25462610
});

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2258,6 +2258,23 @@ export class Clerk implements ClerkInterface {
22582258
if (this.session) {
22592259
const session = this.#getSessionFromClient(this.session.id);
22602260

2261+
const hasTransitionedToPendingStatus = this.session.status === 'active' && session?.status === 'pending';
2262+
if (hasTransitionedToPendingStatus) {
2263+
const onBeforeSetActive: SetActiveHook =
2264+
typeof window !== 'undefined' && typeof window.__unstable__onBeforeSetActive === 'function'
2265+
? window.__unstable__onBeforeSetActive
2266+
: noop;
2267+
2268+
const onAfterSetActive: SetActiveHook =
2269+
typeof window !== 'undefined' && typeof window.__unstable__onAfterSetActive === 'function'
2270+
? window.__unstable__onAfterSetActive
2271+
: noop;
2272+
2273+
// Execute hooks to update server authentication context and trigger
2274+
// page protections in clerkMiddleware or server components
2275+
void onBeforeSetActive()?.then?.(() => void onAfterSetActive());
2276+
}
2277+
22612278
// Note: this might set this.session to null
22622279
this.#setAccessors(session);
22632280

0 commit comments

Comments
 (0)