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
25 changes: 22 additions & 3 deletions src/session.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ import { authkitLoader, encryptSession, terminateSession, refreshSession } from
import { assertIsResponse } from './test-utils/test-helpers.js';
import { getWorkOS } from './workos.js';
import { getConfig } from './config.js';
import { getAuthorizationUrl } from './get-authorization-url.js';

jest.mock('./sessionStorage.js', () => ({
configureSessionStorage: jest.fn(),
getSessionStorage: jest.fn(),
}));

jest.mock('./get-authorization-url.js', () => ({
getAuthorizationUrl: jest.fn(),
}));

// Mock dependencies
const fakeWorkosInstance = {
userManagement: {
Expand All @@ -39,6 +44,7 @@ const authenticateWithRefreshToken = jest.mocked(workos.userManagement.authentic
const getSessionStorage = jest.mocked(getSessionStorageMock);
const configureSessionStorage = jest.mocked(configureSessionStorageMock);
const jwtVerify = jest.mocked(jose.jwtVerify);
const getAuthorizationUrlMock = jest.mocked(getAuthorizationUrl);

function getHeaderValue(headers: HeadersInit | undefined, name: string): string | null {
if (!headers) {
Expand Down Expand Up @@ -113,6 +119,10 @@ describe('session', () => {
destroySession,
commitSession,
});

// Reset getAuthorizationUrl mock
getAuthorizationUrlMock.mockReset();
getAuthorizationUrlMock.mockResolvedValue('https://auth.workos.com/oauth/authorize');
});

describe('encryptSession', () => {
Expand Down Expand Up @@ -594,17 +604,26 @@ describe('session', () => {
expect(getHeaderValue(init?.headers, 'Set-Cookie')).toBe('new-session-cookie');
});

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

// Setup the mock to return a URL with state parameter
getAuthorizationUrlMock.mockResolvedValue('https://auth.workos.com/oauth/authorize?state=abc123');

try {
await authkitLoader(createLoaderArgs(createMockRequest()));
const mockRequest = createMockRequest('test-cookie', 'https://app.example.com/dashboard/settings');
await authkitLoader(createLoaderArgs(mockRequest));
fail('Expected redirect response to be thrown');
} catch (response: unknown) {
assertIsResponse(response);
expect(response.status).toBe(302);
expect(response.headers.get('Location')).toBe('/');
expect(response.headers.get('Location')).toBe('https://auth.workos.com/oauth/authorize?state=abc123');
expect(response.headers.get('Set-Cookie')).toBe('destroyed-session-cookie');

// Verify getAuthorizationUrl was called with the correct returnPathname
expect(getAuthorizationUrlMock).toHaveBeenCalledWith({
returnPathname: '/dashboard/settings',
});
}
});

Expand Down
3 changes: 2 additions & 1 deletion src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,8 @@ export async function authkitLoader<Data = unknown>(
}
}

throw redirect('/', {
const returnPathname = getReturnPathname(request.url);
throw redirect(await getAuthorizationUrl({ returnPathname }), {
headers: {
'Set-Cookie': await destroySession(cookieSession),
},
Expand Down