@workos-inc/authkit-nextjs 2.13.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/esm/auth.js +20 -4
- package/dist/esm/auth.js.map +1 -1
- package/dist/esm/types/auth.d.ts +4 -2
- package/dist/esm/types/session.d.ts +1 -1
- package/dist/esm/types/validate-api-key.d.ts +1 -1
- package/dist/esm/types/workos.d.ts +1 -1
- package/dist/esm/workos.js +1 -1
- package/package.json +20 -21
- package/src/actions.spec.ts +14 -12
- package/src/auth.spec.ts +45 -29
- package/src/auth.ts +22 -2
- package/src/authkit-callback-route.spec.ts +31 -29
- package/src/components/authkit-provider.spec.tsx +31 -31
- package/src/components/button.spec.tsx +4 -6
- package/src/components/impersonation.spec.tsx +25 -25
- package/src/components/min-max-button.spec.tsx +2 -2
- package/src/components/tokenStore.spec.ts +21 -21
- package/src/components/useAccessToken.spec.tsx +73 -77
- package/src/components/useTokenClaims.spec.tsx +22 -22
- package/src/cookie.spec.ts +7 -8
- package/src/get-authorization-url.spec.ts +12 -13
- package/src/session.spec.ts +74 -63
- package/src/utils.spec.ts +14 -31
- package/src/validate-api-key.spec.ts +4 -6
- package/src/workos.spec.ts +2 -2
- package/src/workos.ts +1 -1
|
@@ -3,25 +3,25 @@ import { render, waitFor } from '@testing-library/react';
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { useAuth } from './authkit-provider.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
getAccessTokenAction:
|
|
8
|
-
refreshAccessTokenAction:
|
|
6
|
+
vi.mock('../actions.js', () => ({
|
|
7
|
+
getAccessTokenAction: vi.fn(),
|
|
8
|
+
refreshAccessTokenAction: vi.fn(),
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
const originalModule =
|
|
11
|
+
vi.mock('./authkit-provider.js', async () => {
|
|
12
|
+
const originalModule = await vi.importActual<typeof import('./authkit-provider.js')>('./authkit-provider.js');
|
|
13
13
|
return {
|
|
14
14
|
...originalModule,
|
|
15
|
-
useAuth:
|
|
15
|
+
useAuth: vi.fn(),
|
|
16
16
|
};
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
useAccessToken:
|
|
19
|
+
vi.mock('./useAccessToken.js', () => ({
|
|
20
|
+
useAccessToken: vi.fn(() => ({ accessToken: undefined })),
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
decodeJwt:
|
|
23
|
+
vi.mock('jose', () => ({
|
|
24
|
+
decodeJwt: vi.fn((token: string) => {
|
|
25
25
|
if (token === 'malformed-token' || token === 'throw-error-token') {
|
|
26
26
|
throw new Error('Invalid JWT');
|
|
27
27
|
}
|
|
@@ -42,21 +42,21 @@ import { useTokenClaims } from './useTokenClaims.js';
|
|
|
42
42
|
|
|
43
43
|
describe('useTokenClaims', () => {
|
|
44
44
|
beforeEach(() => {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
vi.clearAllMocks();
|
|
46
|
+
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
47
47
|
|
|
48
|
-
(useAuth as
|
|
48
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
49
49
|
user: { id: 'user_123' },
|
|
50
50
|
sessionId: 'session_123',
|
|
51
|
-
refreshAuth:
|
|
51
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
52
52
|
}));
|
|
53
53
|
|
|
54
54
|
// Reset useAccessToken mock to default
|
|
55
|
-
(useAccessToken as
|
|
55
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: undefined });
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
afterEach(() => {
|
|
59
|
-
|
|
59
|
+
vi.useRealTimers();
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
const TokenClaimsTestComponent = () => {
|
|
@@ -69,7 +69,7 @@ describe('useTokenClaims', () => {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
it('should return empty object when no access token is available', async () => {
|
|
72
|
-
(useAccessToken as
|
|
72
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: undefined });
|
|
73
73
|
|
|
74
74
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
75
75
|
|
|
@@ -101,7 +101,7 @@ describe('useTokenClaims', () => {
|
|
|
101
101
|
};
|
|
102
102
|
const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
103
103
|
|
|
104
|
-
(useAccessToken as
|
|
104
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: token });
|
|
105
105
|
|
|
106
106
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
107
107
|
|
|
@@ -129,7 +129,7 @@ describe('useTokenClaims', () => {
|
|
|
129
129
|
};
|
|
130
130
|
const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
131
131
|
|
|
132
|
-
(useAccessToken as
|
|
132
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: token });
|
|
133
133
|
|
|
134
134
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
135
135
|
|
|
@@ -147,7 +147,7 @@ describe('useTokenClaims', () => {
|
|
|
147
147
|
};
|
|
148
148
|
const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
149
149
|
|
|
150
|
-
(useAccessToken as
|
|
150
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: token });
|
|
151
151
|
|
|
152
152
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
153
153
|
|
|
@@ -175,7 +175,7 @@ describe('useTokenClaims', () => {
|
|
|
175
175
|
};
|
|
176
176
|
const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
177
177
|
|
|
178
|
-
(useAccessToken as
|
|
178
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: token });
|
|
179
179
|
|
|
180
180
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
181
181
|
|
|
@@ -185,7 +185,7 @@ describe('useTokenClaims', () => {
|
|
|
185
185
|
});
|
|
186
186
|
|
|
187
187
|
it('should return empty object when decodeJwt throws an error', async () => {
|
|
188
|
-
(useAccessToken as
|
|
188
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: 'malformed-token' });
|
|
189
189
|
|
|
190
190
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
191
191
|
|
package/src/cookie.spec.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { describe, it, expect } from '@jest/globals';
|
|
2
|
-
|
|
3
|
-
// Mock at the top of the file
|
|
4
|
-
jest.mock('./env-variables');
|
|
5
|
-
|
|
6
1
|
describe('cookie.ts', () => {
|
|
7
2
|
beforeEach(() => {
|
|
8
3
|
// Clear all mocks before each test
|
|
9
|
-
|
|
10
|
-
// Reset modules
|
|
11
|
-
|
|
4
|
+
vi.clearAllMocks();
|
|
5
|
+
// Reset modules to ensure fresh imports
|
|
6
|
+
vi.resetModules();
|
|
7
|
+
// Re-mock env-variables with a fresh copy each time
|
|
8
|
+
vi.doMock('./env-variables', async (importOriginal) => {
|
|
9
|
+
return { ...(await importOriginal<typeof import('./env-variables')>()) };
|
|
10
|
+
});
|
|
12
11
|
});
|
|
13
12
|
|
|
14
13
|
describe('getCookieOptions', () => {
|
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
|
|
2
1
|
import { getAuthorizationUrl } from './get-authorization-url.js';
|
|
3
2
|
import { headers } from 'next/headers';
|
|
4
3
|
import { getWorkOS } from './workos.js';
|
|
5
4
|
|
|
6
|
-
jest.mock('next/headers');
|
|
7
|
-
|
|
8
5
|
// Mock dependencies
|
|
9
|
-
const fakeWorkosInstance = {
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const { fakeWorkosInstance } = vi.hoisted(() => ({
|
|
7
|
+
fakeWorkosInstance: {
|
|
8
|
+
userManagement: {
|
|
9
|
+
getAuthorizationUrl: vi.fn(),
|
|
10
|
+
},
|
|
12
11
|
},
|
|
13
|
-
};
|
|
12
|
+
}));
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
getWorkOS:
|
|
14
|
+
vi.mock('./workos', () => ({
|
|
15
|
+
getWorkOS: vi.fn(() => fakeWorkosInstance),
|
|
17
16
|
}));
|
|
18
17
|
|
|
19
18
|
describe('getAuthorizationUrl', () => {
|
|
20
19
|
const workos = getWorkOS();
|
|
21
20
|
beforeEach(() => {
|
|
22
|
-
|
|
21
|
+
vi.clearAllMocks();
|
|
23
22
|
});
|
|
24
23
|
|
|
25
24
|
it('uses x-redirect-uri header when redirectUri option is not provided', async () => {
|
|
@@ -27,7 +26,7 @@ describe('getAuthorizationUrl', () => {
|
|
|
27
26
|
nextHeaders.set('x-redirect-uri', 'http://test-redirect.com');
|
|
28
27
|
|
|
29
28
|
// Mock workos.userManagement.getAuthorizationUrl
|
|
30
|
-
|
|
29
|
+
vi.mocked(workos.userManagement.getAuthorizationUrl).mockReturnValue('mock-url');
|
|
31
30
|
|
|
32
31
|
await getAuthorizationUrl({});
|
|
33
32
|
|
|
@@ -39,7 +38,7 @@ describe('getAuthorizationUrl', () => {
|
|
|
39
38
|
});
|
|
40
39
|
|
|
41
40
|
it('works when called with no arguments', async () => {
|
|
42
|
-
|
|
41
|
+
vi.mocked(workos.userManagement.getAuthorizationUrl).mockReturnValue('mock-url');
|
|
43
42
|
|
|
44
43
|
await getAuthorizationUrl(); // Call with no arguments
|
|
45
44
|
|
|
@@ -47,7 +46,7 @@ describe('getAuthorizationUrl', () => {
|
|
|
47
46
|
});
|
|
48
47
|
|
|
49
48
|
it('works when prompt is provided', async () => {
|
|
50
|
-
|
|
49
|
+
vi.mocked(workos.userManagement.getAuthorizationUrl).mockReturnValue('mock-url');
|
|
51
50
|
|
|
52
51
|
await getAuthorizationUrl({ prompt: 'consent' });
|
|
53
52
|
|
package/src/session.spec.ts
CHANGED
|
@@ -10,12 +10,15 @@ import { jwtVerify } from 'jose';
|
|
|
10
10
|
import { sealData } from 'iron-session';
|
|
11
11
|
import { User } from '@workos-inc/node';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
vi.mock('jose', async () => {
|
|
14
|
+
const actual = await vi.importActual<typeof import('jose')>('jose');
|
|
15
|
+
return {
|
|
16
|
+
jwtVerify: vi.fn(),
|
|
17
|
+
createRemoteJWKSet: vi.fn(),
|
|
18
|
+
SignJWT: actual.SignJWT,
|
|
19
|
+
decodeJwt: actual.decodeJwt,
|
|
20
|
+
};
|
|
21
|
+
});
|
|
19
22
|
|
|
20
23
|
// logging is disabled by default, flip this to true to still have logs in the console
|
|
21
24
|
const DEBUG = false;
|
|
@@ -47,11 +50,11 @@ describe('session.ts', () => {
|
|
|
47
50
|
} as User,
|
|
48
51
|
};
|
|
49
52
|
|
|
50
|
-
let consoleLogSpy:
|
|
53
|
+
let consoleLogSpy: MockInstance;
|
|
51
54
|
|
|
52
55
|
beforeEach(async () => {
|
|
53
56
|
// Clear all mocks between tests
|
|
54
|
-
|
|
57
|
+
vi.clearAllMocks();
|
|
55
58
|
|
|
56
59
|
// Reset the cookie store
|
|
57
60
|
const nextCookies = await cookies();
|
|
@@ -63,9 +66,9 @@ describe('session.ts', () => {
|
|
|
63
66
|
nextHeaders._reset();
|
|
64
67
|
nextHeaders.set('x-workos-middleware', 'true');
|
|
65
68
|
|
|
66
|
-
(jwtVerify as
|
|
69
|
+
(jwtVerify as Mock).mockReset();
|
|
67
70
|
|
|
68
|
-
consoleLogSpy =
|
|
71
|
+
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation((...args) => {
|
|
69
72
|
if (DEBUG) {
|
|
70
73
|
console.info(...args);
|
|
71
74
|
}
|
|
@@ -74,7 +77,7 @@ describe('session.ts', () => {
|
|
|
74
77
|
|
|
75
78
|
afterEach(() => {
|
|
76
79
|
consoleLogSpy.mockRestore();
|
|
77
|
-
|
|
80
|
+
vi.resetModules();
|
|
78
81
|
});
|
|
79
82
|
|
|
80
83
|
describe('withAuth', () => {
|
|
@@ -159,7 +162,7 @@ describe('session.ts', () => {
|
|
|
159
162
|
it('should throw an error if the redirect URI is not set', async () => {
|
|
160
163
|
const originalWorkosRedirectUri = envVariables.WORKOS_REDIRECT_URI;
|
|
161
164
|
|
|
162
|
-
|
|
165
|
+
Object.defineProperty(envVariables, 'WORKOS_REDIRECT_URI', { value: '', configurable: true });
|
|
163
166
|
|
|
164
167
|
await expect(async () => {
|
|
165
168
|
await updateSessionMiddleware(
|
|
@@ -174,13 +177,16 @@ describe('session.ts', () => {
|
|
|
174
177
|
);
|
|
175
178
|
}).rejects.toThrow('You must provide a redirect URI in the AuthKit middleware or in the environment variables.');
|
|
176
179
|
|
|
177
|
-
|
|
180
|
+
Object.defineProperty(envVariables, 'WORKOS_REDIRECT_URI', {
|
|
181
|
+
value: originalWorkosRedirectUri,
|
|
182
|
+
configurable: true,
|
|
183
|
+
});
|
|
178
184
|
});
|
|
179
185
|
|
|
180
186
|
it('should throw an error if the cookie password is not set', async () => {
|
|
181
187
|
const originalWorkosCookiePassword = envVariables.WORKOS_COOKIE_PASSWORD;
|
|
182
188
|
|
|
183
|
-
|
|
189
|
+
Object.defineProperty(envVariables, 'WORKOS_COOKIE_PASSWORD', { value: '', configurable: true });
|
|
184
190
|
|
|
185
191
|
await expect(async () => {
|
|
186
192
|
await updateSessionMiddleware(
|
|
@@ -197,13 +203,16 @@ describe('session.ts', () => {
|
|
|
197
203
|
'You must provide a valid cookie password that is at least 32 characters in the environment variables.',
|
|
198
204
|
);
|
|
199
205
|
|
|
200
|
-
|
|
206
|
+
Object.defineProperty(envVariables, 'WORKOS_COOKIE_PASSWORD', {
|
|
207
|
+
value: originalWorkosCookiePassword,
|
|
208
|
+
configurable: true,
|
|
209
|
+
});
|
|
201
210
|
});
|
|
202
211
|
|
|
203
212
|
it('should throw an error if the cookie password is less than 32 characters', async () => {
|
|
204
213
|
const originalWorkosCookiePassword = envVariables.WORKOS_COOKIE_PASSWORD;
|
|
205
214
|
|
|
206
|
-
|
|
215
|
+
Object.defineProperty(envVariables, 'WORKOS_COOKIE_PASSWORD', { value: 'short', configurable: true });
|
|
207
216
|
|
|
208
217
|
await expect(async () => {
|
|
209
218
|
await updateSessionMiddleware(
|
|
@@ -220,7 +229,10 @@ describe('session.ts', () => {
|
|
|
220
229
|
'You must provide a valid cookie password that is at least 32 characters in the environment variables.',
|
|
221
230
|
);
|
|
222
231
|
|
|
223
|
-
|
|
232
|
+
Object.defineProperty(envVariables, 'WORKOS_COOKIE_PASSWORD', {
|
|
233
|
+
value: originalWorkosCookiePassword,
|
|
234
|
+
configurable: true,
|
|
235
|
+
});
|
|
224
236
|
});
|
|
225
237
|
|
|
226
238
|
it('should return early if there is no session', async () => {
|
|
@@ -241,7 +253,7 @@ describe('session.ts', () => {
|
|
|
241
253
|
});
|
|
242
254
|
|
|
243
255
|
it('should return 200 if the session is valid', async () => {
|
|
244
|
-
|
|
256
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
245
257
|
|
|
246
258
|
const nextCookies = await cookies();
|
|
247
259
|
nextCookies.set(
|
|
@@ -249,7 +261,7 @@ describe('session.ts', () => {
|
|
|
249
261
|
await sealData(mockSession, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
|
|
250
262
|
);
|
|
251
263
|
|
|
252
|
-
(jwtVerify as
|
|
264
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
253
265
|
return true;
|
|
254
266
|
});
|
|
255
267
|
|
|
@@ -272,11 +284,11 @@ describe('session.ts', () => {
|
|
|
272
284
|
it('should attempt to refresh the session when the access token is invalid', async () => {
|
|
273
285
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
274
286
|
|
|
275
|
-
(jwtVerify as
|
|
287
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
276
288
|
throw new Error('Invalid token');
|
|
277
289
|
});
|
|
278
290
|
|
|
279
|
-
|
|
291
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
280
292
|
accessToken: await generateTestToken(),
|
|
281
293
|
refreshToken: 'new-refresh-token',
|
|
282
294
|
user: mockSession.user,
|
|
@@ -308,17 +320,15 @@ describe('session.ts', () => {
|
|
|
308
320
|
});
|
|
309
321
|
|
|
310
322
|
it('should delete the cookie when refreshing fails', async () => {
|
|
311
|
-
|
|
323
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
312
324
|
|
|
313
325
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
314
326
|
|
|
315
|
-
(jwtVerify as
|
|
327
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
316
328
|
throw new Error('Invalid token');
|
|
317
329
|
});
|
|
318
330
|
|
|
319
|
-
|
|
320
|
-
.spyOn(workos.userManagement, 'authenticateWithRefreshToken')
|
|
321
|
-
.mockRejectedValue(new Error('Failed to refresh'));
|
|
331
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(new Error('Failed to refresh'));
|
|
322
332
|
|
|
323
333
|
const request = new NextRequest(new URL('http://example.com'));
|
|
324
334
|
|
|
@@ -354,7 +364,7 @@ describe('session.ts', () => {
|
|
|
354
364
|
|
|
355
365
|
describe('middleware auth', () => {
|
|
356
366
|
it('should redirect unauthenticated users on protected routes', async () => {
|
|
357
|
-
|
|
367
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
358
368
|
|
|
359
369
|
const request = new NextRequest(new URL('http://example.com/protected'));
|
|
360
370
|
const result = await updateSessionMiddleware(
|
|
@@ -476,11 +486,11 @@ describe('session.ts', () => {
|
|
|
476
486
|
|
|
477
487
|
it('should throw an error if the provided regex is invalid and a non-Error object is thrown', async () => {
|
|
478
488
|
// Reset modules to ensure clean import state
|
|
479
|
-
|
|
489
|
+
vi.resetModules();
|
|
480
490
|
|
|
481
491
|
// Import first, then spy
|
|
482
492
|
const pathToRegexp = await import('path-to-regexp');
|
|
483
|
-
const parseSpy =
|
|
493
|
+
const parseSpy = vi.spyOn(pathToRegexp, 'parse').mockImplementation(() => {
|
|
484
494
|
throw 'invalid regex';
|
|
485
495
|
});
|
|
486
496
|
|
|
@@ -504,6 +514,9 @@ describe('session.ts', () => {
|
|
|
504
514
|
|
|
505
515
|
// Verify the mock was called
|
|
506
516
|
expect(parseSpy).toHaveBeenCalled();
|
|
517
|
+
|
|
518
|
+
// Restore the spy to prevent leaking to subsequent tests
|
|
519
|
+
parseSpy.mockRestore();
|
|
507
520
|
});
|
|
508
521
|
|
|
509
522
|
it('should default to the WORKOS_REDIRECT_URI environment variable if no redirect URI is provided', async () => {
|
|
@@ -523,17 +536,17 @@ describe('session.ts', () => {
|
|
|
523
536
|
});
|
|
524
537
|
|
|
525
538
|
it('should delete the cookie and redirect when refreshing fails', async () => {
|
|
526
|
-
|
|
539
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
527
540
|
|
|
528
541
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
529
542
|
|
|
530
|
-
(jwtVerify as
|
|
543
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
531
544
|
throw new Error('Invalid token');
|
|
532
545
|
});
|
|
533
546
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
547
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(
|
|
548
|
+
new Error('Failed to refresh'),
|
|
549
|
+
);
|
|
537
550
|
|
|
538
551
|
const request = new NextRequest(new URL('http://example.com'));
|
|
539
552
|
|
|
@@ -634,12 +647,12 @@ describe('session.ts', () => {
|
|
|
634
647
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
635
648
|
|
|
636
649
|
// Mock token verification to fail
|
|
637
|
-
(jwtVerify as
|
|
650
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
638
651
|
throw new Error('Invalid token');
|
|
639
652
|
});
|
|
640
653
|
|
|
641
654
|
// Mock successful refresh
|
|
642
|
-
|
|
655
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
643
656
|
accessToken: await generateTestToken(),
|
|
644
657
|
refreshToken: 'new-refresh-token',
|
|
645
658
|
user: mockSession.user,
|
|
@@ -667,12 +680,12 @@ describe('session.ts', () => {
|
|
|
667
680
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
668
681
|
|
|
669
682
|
// Mock token verification to fail
|
|
670
|
-
(jwtVerify as
|
|
683
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
671
684
|
throw new Error('Invalid token');
|
|
672
685
|
});
|
|
673
686
|
|
|
674
687
|
// Mock refresh failure
|
|
675
|
-
|
|
688
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(new Error('Refresh failed'));
|
|
676
689
|
|
|
677
690
|
const request = new NextRequest(new URL('http://example.com/protected'));
|
|
678
691
|
request.cookies.set(
|
|
@@ -694,15 +707,15 @@ describe('session.ts', () => {
|
|
|
694
707
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
695
708
|
|
|
696
709
|
// Mock token verification to fail
|
|
697
|
-
(jwtVerify as
|
|
710
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
698
711
|
throw new Error('Invalid token');
|
|
699
712
|
});
|
|
700
713
|
|
|
701
714
|
const newAccessToken = await generateTestToken();
|
|
702
|
-
const mockSuccessCallback =
|
|
715
|
+
const mockSuccessCallback = vi.fn();
|
|
703
716
|
|
|
704
717
|
// Mock successful refresh
|
|
705
|
-
|
|
718
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
706
719
|
accessToken: newAccessToken,
|
|
707
720
|
refreshToken: 'new-refresh-token',
|
|
708
721
|
user: mockSession.user,
|
|
@@ -733,15 +746,15 @@ describe('session.ts', () => {
|
|
|
733
746
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
734
747
|
|
|
735
748
|
// Mock token verification to fail
|
|
736
|
-
(jwtVerify as
|
|
749
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
737
750
|
throw new Error('Invalid token');
|
|
738
751
|
});
|
|
739
752
|
|
|
740
753
|
const mockError = new Error('Refresh failed');
|
|
741
|
-
const mockErrorCallback =
|
|
754
|
+
const mockErrorCallback = vi.fn();
|
|
742
755
|
|
|
743
756
|
// Mock refresh failure
|
|
744
|
-
|
|
757
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(mockError);
|
|
745
758
|
|
|
746
759
|
const request = new NextRequest(new URL('http://example.com/protected'));
|
|
747
760
|
request.cookies.set(
|
|
@@ -764,15 +777,15 @@ describe('session.ts', () => {
|
|
|
764
777
|
|
|
765
778
|
describe('refreshSession', () => {
|
|
766
779
|
it('should refresh session successfully', async () => {
|
|
767
|
-
|
|
780
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
768
781
|
accessToken: await generateTestToken(),
|
|
769
782
|
refreshToken: 'new-refresh-token',
|
|
770
783
|
user: mockSession.user,
|
|
771
784
|
});
|
|
772
785
|
|
|
773
|
-
|
|
774
|
-
.
|
|
775
|
-
|
|
786
|
+
vi.spyOn(workos.userManagement, 'getJwksUrl').mockReturnValue(
|
|
787
|
+
'https://api.workos.com/sso/jwks/client_1234567890',
|
|
788
|
+
);
|
|
776
789
|
|
|
777
790
|
const nextCookies = await cookies();
|
|
778
791
|
nextCookies.set(
|
|
@@ -808,15 +821,15 @@ describe('session.ts', () => {
|
|
|
808
821
|
await sealData(mockSession, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
|
|
809
822
|
);
|
|
810
823
|
|
|
811
|
-
|
|
824
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
812
825
|
accessToken: await generateTestToken({ org_id: 'org_456' }),
|
|
813
826
|
refreshToken: 'new-refresh-token',
|
|
814
827
|
user: mockSession.user,
|
|
815
828
|
});
|
|
816
829
|
|
|
817
|
-
|
|
818
|
-
.
|
|
819
|
-
|
|
830
|
+
vi.spyOn(workos.userManagement, 'getJwksUrl').mockReturnValue(
|
|
831
|
+
'https://api.workos.com/sso/jwks/client_1234567890',
|
|
832
|
+
);
|
|
820
833
|
|
|
821
834
|
const result = await refreshSession({ organizationId: 'org_456' });
|
|
822
835
|
|
|
@@ -835,8 +848,8 @@ describe('session.ts', () => {
|
|
|
835
848
|
'wos-session',
|
|
836
849
|
await sealData(mockSessionWithValidJWT, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
|
|
837
850
|
);
|
|
838
|
-
|
|
839
|
-
expect(refreshSession({ ensureSignedIn: false })).rejects.toThrow('Failed to refresh session: fail');
|
|
851
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue('fail');
|
|
852
|
+
await expect(refreshSession({ ensureSignedIn: false })).rejects.toThrow('Failed to refresh session: fail');
|
|
840
853
|
});
|
|
841
854
|
|
|
842
855
|
it('throws if authenticateWithRefreshToken fails with error', async () => {
|
|
@@ -850,7 +863,7 @@ describe('session.ts', () => {
|
|
|
850
863
|
'wos-session',
|
|
851
864
|
await sealData(mockSessionWithValidJWT, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
|
|
852
865
|
);
|
|
853
|
-
|
|
866
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(new Error('error'));
|
|
854
867
|
await expect(refreshSession()).rejects.toThrow('Failed to refresh session: error');
|
|
855
868
|
});
|
|
856
869
|
});
|
|
@@ -860,7 +873,7 @@ describe('session.ts', () => {
|
|
|
860
873
|
const nextCookies = await cookies();
|
|
861
874
|
// @ts-expect-error - _reset is part of the mock
|
|
862
875
|
nextCookies._reset();
|
|
863
|
-
|
|
876
|
+
vi.clearAllMocks();
|
|
864
877
|
});
|
|
865
878
|
|
|
866
879
|
it('should return all token claims when accessToken is provided', async () => {
|
|
@@ -954,7 +967,7 @@ describe('session.ts', () => {
|
|
|
954
967
|
|
|
955
968
|
describe('eager auth functionality', () => {
|
|
956
969
|
beforeEach(() => {
|
|
957
|
-
|
|
970
|
+
vi.clearAllMocks();
|
|
958
971
|
});
|
|
959
972
|
|
|
960
973
|
describe('isInitialDocumentRequest', () => {
|
|
@@ -1048,12 +1061,12 @@ describe('session.ts', () => {
|
|
|
1048
1061
|
// Setup invalid session that needs refresh
|
|
1049
1062
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
1050
1063
|
|
|
1051
|
-
(jwtVerify as
|
|
1064
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
1052
1065
|
throw new Error('Invalid token');
|
|
1053
1066
|
});
|
|
1054
1067
|
|
|
1055
1068
|
const newAccessToken = await generateTestToken();
|
|
1056
|
-
|
|
1069
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
1057
1070
|
accessToken: newAccessToken,
|
|
1058
1071
|
refreshToken: 'new-refresh-token',
|
|
1059
1072
|
user: mockSession.user,
|
|
@@ -1079,13 +1092,11 @@ describe('session.ts', () => {
|
|
|
1079
1092
|
// Setup invalid session
|
|
1080
1093
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
1081
1094
|
|
|
1082
|
-
(jwtVerify as
|
|
1095
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
1083
1096
|
throw new Error('Invalid token');
|
|
1084
1097
|
});
|
|
1085
1098
|
|
|
1086
|
-
|
|
1087
|
-
.spyOn(workos.userManagement, 'authenticateWithRefreshToken')
|
|
1088
|
-
.mockRejectedValue(new Error('Refresh failed'));
|
|
1099
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(new Error('Refresh failed'));
|
|
1089
1100
|
|
|
1090
1101
|
const request = new NextRequest(new URL('http://example.com/page'));
|
|
1091
1102
|
request.headers.set('accept', 'text/html');
|