Skip to content

Commit cee5925

Browse files
authored
Fix: Preserve return pathname on session refresh failure (#71)
1 parent 233d6c9 commit cee5925

File tree

2 files changed

+24
-4
lines changed

2 files changed

+24
-4
lines changed

src/session.spec.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@ import { authkitLoader, encryptSession, refreshSession, terminateSession } from
1111
import { assertIsResponse } from './test-utils/test-helpers.js';
1212
import { getWorkOS } from './workos.js';
1313
import { getConfig } from './config.js';
14+
import { getAuthorizationUrl } from './get-authorization-url.js';
1415

1516
jest.mock('./sessionStorage.js', () => ({
1617
configureSessionStorage: jest.fn(),
1718
getSessionStorage: jest.fn(),
1819
}));
1920

21+
jest.mock('./get-authorization-url.js', () => ({
22+
getAuthorizationUrl: jest.fn(),
23+
}));
24+
2025
// Mock dependencies
2126
const fakeWorkosInstance = {
2227
userManagement: {
@@ -39,6 +44,7 @@ const authenticateWithRefreshToken = jest.mocked(workos.userManagement.authentic
3944
const getSessionStorage = jest.mocked(getSessionStorageMock);
4045
const configureSessionStorage = jest.mocked(configureSessionStorageMock);
4146
const jwtVerify = jest.mocked(jose.jwtVerify);
47+
const getAuthorizationUrlMock = jest.mocked(getAuthorizationUrl);
4248

4349
function getHeaderValue(headers: HeadersInit | undefined, name: string): string | null {
4450
if (!headers) {
@@ -113,6 +119,10 @@ describe('session', () => {
113119
destroySession,
114120
commitSession,
115121
});
122+
123+
// Reset getAuthorizationUrl mock
124+
getAuthorizationUrlMock.mockReset();
125+
getAuthorizationUrlMock.mockResolvedValue('https://auth.workos.com/oauth/authorize');
116126
});
117127

118128
describe('encryptSession', () => {
@@ -793,17 +803,26 @@ describe('session', () => {
793803
expect(onSessionRefreshSuccess).toHaveBeenCalled();
794804
});
795805

796-
it('should redirect to root when refresh fails', async () => {
806+
it('should redirect to authorization URL preserving returnPathname when refresh fails', async () => {
797807
authenticateWithRefreshToken.mockRejectedValue(new Error('Refresh token invalid'));
798808

809+
// Setup the mock to return a URL with state parameter
810+
getAuthorizationUrlMock.mockResolvedValue('https://auth.workos.com/oauth/authorize?state=abc123');
811+
799812
try {
800-
await authkitLoader(createLoaderArgs(createMockRequest()));
813+
const mockRequest = createMockRequest('test-cookie', 'https://app.example.com/dashboard/settings');
814+
await authkitLoader(createLoaderArgs(mockRequest));
801815
fail('Expected redirect response to be thrown');
802816
} catch (response: unknown) {
803817
assertIsResponse(response);
804818
expect(response.status).toBe(302);
805-
expect(response.headers.get('Location')).toBe('/');
819+
expect(response.headers.get('Location')).toBe('https://auth.workos.com/oauth/authorize?state=abc123');
806820
expect(response.headers.get('Set-Cookie')).toBe('destroyed-session-cookie');
821+
822+
// Verify getAuthorizationUrl was called with the correct returnPathname
823+
expect(getAuthorizationUrlMock).toHaveBeenCalledWith({
824+
returnPathname: '/dashboard/settings',
825+
});
807826
}
808827
});
809828

src/session.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,8 @@ export async function authkitLoader<Data = unknown>(
388388
}
389389
}
390390

391-
throw redirect('/', {
391+
const returnPathname = getReturnPathname(request.url);
392+
throw redirect(await getAuthorizationUrl({ returnPathname }), {
392393
headers: {
393394
'Set-Cookie': await destroySession(cookieSession),
394395
},

0 commit comments

Comments
 (0)