Skip to content

Commit 1cedc06

Browse files
Fix clearSession operation order to prevent premature user state clearing (#1309)
1 parent d1f7861 commit 1cedc06

File tree

2 files changed

+88
-5
lines changed

2 files changed

+88
-5
lines changed

src/hooks/Auth0Provider.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,14 @@ export const Auth0Provider = ({
128128
opts?: NativeClearSessionOptions
129129
): Promise<void> => {
130130
try {
131-
// Step 1: Clear the locally stored credentials first.
131+
// Step 1: Clear the server-side session cookie.
132+
await client.webAuth.clearSession(parameters, opts);
133+
134+
// Step 2: Clear the locally stored credentials first.
132135
await client.credentialsManager.clearCredentials();
133136

134-
// Step 2: Update the React state immediately for a snappy UX.
137+
// Step 3: Update the React state immediately for a snappy UX.
135138
dispatch({ type: 'LOGOUT_COMPLETE' });
136-
137-
// Step 3: Clear the server-side session cookie.
138-
await client.webAuth.clearSession(parameters, opts);
139139
} catch (e) {
140140
const error = e as AuthError;
141141
dispatch({ type: 'ERROR', error });

src/hooks/__tests__/Auth0Provider.spec.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,89 @@ describe('Auth0Provider', () => {
332332
expect(mockClientInstance.webAuth.clearSession).toHaveBeenCalled();
333333
});
334334

335+
it('should call clearSession operations in the correct order', async () => {
336+
// Start with a logged-in state
337+
mockClientInstance.credentialsManager.getCredentials.mockResolvedValueOnce({
338+
idToken: 'a.b.c',
339+
accessToken: 'access-token-123',
340+
tokenType: 'Bearer',
341+
expiresAt: Date.now() / 1000 + 3600,
342+
} as any);
343+
344+
// Track the order of calls
345+
const callOrder: string[] = [];
346+
347+
// Mock clearSession to track when it's called
348+
mockClientInstance.webAuth.clearSession.mockImplementation(async () => {
349+
callOrder.push('webAuth.clearSession');
350+
});
351+
352+
// Mock clearCredentials to track when it's called
353+
mockClientInstance.credentialsManager.clearCredentials.mockImplementation(
354+
async () => {
355+
callOrder.push('credentialsManager.clearCredentials');
356+
}
357+
);
358+
359+
let componentRef: any;
360+
const TestConsumerWithRef = () => {
361+
const auth0Context = useAuth0();
362+
componentRef = auth0Context;
363+
return <TestConsumer />;
364+
};
365+
366+
await act(async () => {
367+
render(
368+
<Auth0Provider domain="test.com" clientId="123">
369+
<TestConsumerWithRef />
370+
</Auth0Provider>
371+
);
372+
});
373+
374+
await waitFor(() =>
375+
expect(screen.getByTestId('user-status')).toHaveTextContent(
376+
'Logged in as: Test User'
377+
)
378+
);
379+
380+
// Track when the user state changes from logged-in to logged-out
381+
const originalUser = componentRef.user;
382+
expect(originalUser).toBeTruthy();
383+
384+
const logoutButton = screen.getByTestId('logout-button');
385+
await act(async () => {
386+
fireEvent.click(logoutButton);
387+
});
388+
389+
// Wait for the logout to complete and check the order
390+
await waitFor(() => {
391+
expect(screen.getByTestId('user-status')).toHaveTextContent(
392+
'Not logged in'
393+
);
394+
395+
// At this point, the dispatch action should have been triggered
396+
callOrder.push('dispatch.LOGOUT_COMPLETE');
397+
});
398+
399+
// Verify that the operations happened in the correct order:
400+
// 1. webAuth.clearSession (server-side session)
401+
// 2. credentialsManager.clearCredentials (local credentials)
402+
// 3. dispatch LOGOUT_COMPLETE action (React state update)
403+
expect(callOrder).toEqual([
404+
'webAuth.clearSession',
405+
'credentialsManager.clearCredentials',
406+
'dispatch.LOGOUT_COMPLETE',
407+
]);
408+
409+
expect(mockClientInstance.webAuth.clearSession).toHaveBeenCalled();
410+
expect(
411+
mockClientInstance.credentialsManager.clearCredentials
412+
).toHaveBeenCalled();
413+
414+
// Verify the user state has been cleared
415+
expect(componentRef.user).toBeNull();
416+
});
417+
335418
it('should update the state correctly after a clearCredentials call', async () => {
336419
// Start with a logged-in state
337420
mockClientInstance.credentialsManager.getCredentials.mockResolvedValueOnce({

0 commit comments

Comments
 (0)