@workos-inc/authkit-nextjs 3.0.0-beta.1 → 3.0.1
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 +305 -102
- package/dist/esm/actions.js +35 -5
- package/dist/esm/actions.js.map +1 -1
- package/dist/esm/auth.js +71 -21
- package/dist/esm/auth.js.map +1 -1
- package/dist/esm/authkit-callback-route.js +90 -92
- package/dist/esm/authkit-callback-route.js.map +1 -1
- package/dist/esm/components/authkit-provider.js +36 -15
- package/dist/esm/components/authkit-provider.js.map +1 -1
- package/dist/esm/components/impersonation.js +17 -15
- package/dist/esm/components/impersonation.js.map +1 -1
- package/dist/esm/components/min-max-button.js +1 -1
- package/dist/esm/components/min-max-button.js.map +1 -1
- package/dist/esm/components/tokenStore.js +28 -19
- package/dist/esm/components/tokenStore.js.map +1 -1
- package/dist/esm/components/useAccessToken.js +1 -1
- package/dist/esm/components/useAccessToken.js.map +1 -1
- package/dist/esm/components/useTokenClaims.js +1 -1
- package/dist/esm/components/useTokenClaims.js.map +1 -1
- package/dist/esm/cookie.js +20 -5
- package/dist/esm/cookie.js.map +1 -1
- package/dist/esm/env-variables.js +6 -6
- package/dist/esm/env-variables.js.map +1 -1
- package/dist/esm/errors.js +36 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/get-authorization-url.js +51 -12
- package/dist/esm/get-authorization-url.js.map +1 -1
- package/dist/esm/index.js +5 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/interfaces.js +7 -1
- package/dist/esm/interfaces.js.map +1 -1
- package/dist/esm/middleware-helpers.js +102 -0
- package/dist/esm/middleware-helpers.js.map +1 -0
- package/dist/esm/middleware.js +3 -1
- package/dist/esm/middleware.js.map +1 -1
- package/dist/esm/pkce.js +52 -0
- package/dist/esm/pkce.js.map +1 -0
- package/dist/esm/session.js +82 -35
- package/dist/esm/session.js.map +1 -1
- package/dist/esm/test-helpers.js +1 -1
- package/dist/esm/test-helpers.js.map +1 -1
- package/dist/esm/types/actions.d.ts +34 -5
- package/dist/esm/types/auth.d.ts +7 -15
- package/dist/esm/types/components/authkit-provider.d.ts +6 -2
- package/dist/esm/types/components/impersonation.d.ts +2 -1
- package/dist/esm/types/cookie.d.ts +9 -0
- package/dist/esm/types/env-variables.d.ts +2 -1
- package/dist/esm/types/errors.d.ts +15 -0
- package/dist/esm/types/get-authorization-url.d.ts +2 -2
- package/dist/esm/types/index.d.ts +5 -2
- package/dist/esm/types/interfaces.d.ts +12 -0
- package/dist/esm/types/jwt.d.ts +9 -9
- package/dist/esm/types/middleware-helpers.d.ts +27 -0
- package/dist/esm/types/middleware.d.ts +3 -1
- package/dist/esm/types/pkce.d.ts +17 -0
- package/dist/esm/types/session.d.ts +1 -1
- package/dist/esm/types/utils.d.ts +5 -0
- package/dist/esm/types/validate-api-key.d.ts +1 -0
- package/dist/esm/types/workos.d.ts +1 -1
- package/dist/esm/utils.js +10 -2
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/validate-api-key.js +16 -0
- package/dist/esm/validate-api-key.js.map +1 -0
- package/dist/esm/workos.js +1 -1
- package/package.json +33 -34
- package/src/actions.spec.ts +91 -18
- package/src/actions.ts +44 -6
- package/src/auth.spec.ts +79 -29
- package/src/auth.ts +74 -42
- package/src/authkit-callback-route.spec.ts +372 -58
- package/src/authkit-callback-route.ts +121 -103
- package/src/components/authkit-provider.spec.tsx +264 -70
- package/src/components/authkit-provider.tsx +40 -15
- package/src/components/button.spec.tsx +4 -6
- package/src/components/impersonation.spec.tsx +152 -35
- package/src/components/impersonation.tsx +37 -30
- package/src/components/min-max-button.spec.tsx +2 -1
- package/src/components/tokenStore.spec.ts +59 -44
- package/src/components/tokenStore.ts +11 -3
- package/src/components/useAccessToken.spec.tsx +82 -83
- package/src/components/useTokenClaims.spec.tsx +23 -22
- package/src/cookie.spec.ts +63 -9
- package/src/cookie.ts +35 -0
- package/src/env-variables.ts +2 -0
- package/src/errors.spec.ts +108 -0
- package/src/errors.ts +46 -0
- package/src/get-authorization-url.spec.ts +170 -15
- package/src/get-authorization-url.ts +69 -23
- package/src/index.ts +20 -2
- package/src/interfaces.ts +15 -0
- package/src/jwt.ts +9 -9
- package/src/middleware-helpers.spec.ts +238 -0
- package/src/middleware-helpers.ts +134 -0
- package/src/middleware.spec.ts +25 -0
- package/src/middleware.ts +4 -1
- package/src/pkce.spec.ts +146 -0
- package/src/pkce.ts +59 -0
- package/src/session.spec.ts +87 -89
- package/src/session.ts +104 -27
- package/src/test-helpers.ts +1 -1
- package/src/utils.spec.ts +14 -31
- package/src/utils.ts +9 -0
- package/src/validate-api-key.spec.ts +111 -0
- package/src/validate-api-key.ts +19 -0
- package/src/workos.spec.ts +2 -2
- package/src/workos.ts +1 -1
package/src/session.spec.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Mock, MockInstance } from 'vitest';
|
|
1
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
3
|
import { cookies, headers } from 'next/headers';
|
|
3
4
|
import { redirect } from 'next/navigation';
|
|
@@ -7,15 +8,24 @@ import { getWorkOS } from './workos.js';
|
|
|
7
8
|
import * as envVariables from './env-variables.js';
|
|
8
9
|
|
|
9
10
|
import { jwtVerify } from 'jose';
|
|
11
|
+
|
|
12
|
+
// Helper to override env variable exports without triggering no-import-assign on the import binding
|
|
13
|
+
function setEnvVar(mod: Record<string, unknown>, key: string, value: unknown) {
|
|
14
|
+
Object.defineProperty(mod, key, { value, configurable: true });
|
|
15
|
+
}
|
|
10
16
|
import { sealData } from 'iron-session';
|
|
11
17
|
import { User } from '@workos-inc/node';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
import { getStateFromPKCECookieValue } from './pkce.js';
|
|
19
|
+
|
|
20
|
+
vi.mock('jose', async () => {
|
|
21
|
+
const actual = await vi.importActual<typeof import('jose')>('jose');
|
|
22
|
+
return {
|
|
23
|
+
jwtVerify: vi.fn(),
|
|
24
|
+
createRemoteJWKSet: vi.fn(),
|
|
25
|
+
SignJWT: actual.SignJWT,
|
|
26
|
+
decodeJwt: actual.decodeJwt,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
19
29
|
|
|
20
30
|
// logging is disabled by default, flip this to true to still have logs in the console
|
|
21
31
|
const DEBUG = false;
|
|
@@ -42,20 +52,16 @@ describe('session.ts', () => {
|
|
|
42
52
|
profilePictureUrl: null,
|
|
43
53
|
firstName: null,
|
|
44
54
|
lastName: null,
|
|
45
|
-
lastSignInAt: null,
|
|
46
|
-
locale: null,
|
|
47
|
-
externalId: null,
|
|
48
|
-
metadata: {},
|
|
49
55
|
createdAt: '2024-01-01',
|
|
50
56
|
updatedAt: '2024-01-01',
|
|
51
57
|
} as User,
|
|
52
58
|
};
|
|
53
59
|
|
|
54
|
-
let consoleLogSpy:
|
|
60
|
+
let consoleLogSpy: MockInstance;
|
|
55
61
|
|
|
56
62
|
beforeEach(async () => {
|
|
57
63
|
// Clear all mocks between tests
|
|
58
|
-
|
|
64
|
+
vi.clearAllMocks();
|
|
59
65
|
|
|
60
66
|
// Reset the cookie store
|
|
61
67
|
const nextCookies = await cookies();
|
|
@@ -67,9 +73,9 @@ describe('session.ts', () => {
|
|
|
67
73
|
nextHeaders._reset();
|
|
68
74
|
nextHeaders.set('x-workos-middleware', 'true');
|
|
69
75
|
|
|
70
|
-
(jwtVerify as
|
|
76
|
+
(jwtVerify as Mock).mockReset();
|
|
71
77
|
|
|
72
|
-
consoleLogSpy =
|
|
78
|
+
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation((...args) => {
|
|
73
79
|
if (DEBUG) {
|
|
74
80
|
console.info(...args);
|
|
75
81
|
}
|
|
@@ -78,7 +84,7 @@ describe('session.ts', () => {
|
|
|
78
84
|
|
|
79
85
|
afterEach(() => {
|
|
80
86
|
consoleLogSpy.mockRestore();
|
|
81
|
-
|
|
87
|
+
vi.resetModules();
|
|
82
88
|
});
|
|
83
89
|
|
|
84
90
|
describe('withAuth', () => {
|
|
@@ -120,7 +126,7 @@ describe('session.ts', () => {
|
|
|
120
126
|
await expect(async () => {
|
|
121
127
|
await withAuth();
|
|
122
128
|
}).rejects.toThrow(
|
|
123
|
-
|
|
129
|
+
/You are calling 'withAuth' on https:\/\/example\.com\/ that isn't covered by the AuthKit middleware/,
|
|
124
130
|
);
|
|
125
131
|
});
|
|
126
132
|
|
|
@@ -130,9 +136,7 @@ describe('session.ts', () => {
|
|
|
130
136
|
|
|
131
137
|
await expect(async () => {
|
|
132
138
|
await withAuth({ ensureSignedIn: true });
|
|
133
|
-
}).rejects.toThrow(
|
|
134
|
-
"You are calling 'withAuth' on a route that isn’t covered by the AuthKit middleware. Make sure it is running on all paths you are calling 'withAuth' from by updating your middleware config in 'middleware.(js|ts)'.",
|
|
135
|
-
);
|
|
139
|
+
}).rejects.toThrow(/You are calling 'withAuth' on a route that isn't covered by the AuthKit middleware/);
|
|
136
140
|
});
|
|
137
141
|
|
|
138
142
|
it('should throw an error if the URL is not found in the headers', async () => {
|
|
@@ -150,14 +154,12 @@ describe('session.ts', () => {
|
|
|
150
154
|
|
|
151
155
|
await withAuth({ ensureSignedIn: true });
|
|
152
156
|
|
|
153
|
-
//
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
.replace(/\//g, '_'),
|
|
158
|
-
);
|
|
157
|
+
// The state is now sealed, se we need to unseal it
|
|
158
|
+
const redirectUrl = new URL((redirect as unknown as Mock).mock.calls[0][0]);
|
|
159
|
+
const sealedState = redirectUrl.searchParams.get('state')!;
|
|
160
|
+
const { returnPathname } = await getStateFromPKCECookieValue(sealedState);
|
|
159
161
|
|
|
160
|
-
expect(
|
|
162
|
+
expect(returnPathname).toBe('/protected?test=123');
|
|
161
163
|
});
|
|
162
164
|
});
|
|
163
165
|
|
|
@@ -165,7 +167,7 @@ describe('session.ts', () => {
|
|
|
165
167
|
it('should throw an error if the redirect URI is not set', async () => {
|
|
166
168
|
const originalWorkosRedirectUri = envVariables.WORKOS_REDIRECT_URI;
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
setEnvVar(envVariables, 'WORKOS_REDIRECT_URI', '');
|
|
169
171
|
|
|
170
172
|
await expect(async () => {
|
|
171
173
|
await updateSessionMiddleware(
|
|
@@ -180,13 +182,13 @@ describe('session.ts', () => {
|
|
|
180
182
|
);
|
|
181
183
|
}).rejects.toThrow('You must provide a redirect URI in the AuthKit middleware or in the environment variables.');
|
|
182
184
|
|
|
183
|
-
|
|
185
|
+
setEnvVar(envVariables, 'WORKOS_REDIRECT_URI', originalWorkosRedirectUri);
|
|
184
186
|
});
|
|
185
187
|
|
|
186
188
|
it('should throw an error if the cookie password is not set', async () => {
|
|
187
189
|
const originalWorkosCookiePassword = envVariables.WORKOS_COOKIE_PASSWORD;
|
|
188
190
|
|
|
189
|
-
|
|
191
|
+
setEnvVar(envVariables, 'WORKOS_COOKIE_PASSWORD', '');
|
|
190
192
|
|
|
191
193
|
await expect(async () => {
|
|
192
194
|
await updateSessionMiddleware(
|
|
@@ -203,13 +205,13 @@ describe('session.ts', () => {
|
|
|
203
205
|
'You must provide a valid cookie password that is at least 32 characters in the environment variables.',
|
|
204
206
|
);
|
|
205
207
|
|
|
206
|
-
|
|
208
|
+
setEnvVar(envVariables, 'WORKOS_COOKIE_PASSWORD', originalWorkosCookiePassword);
|
|
207
209
|
});
|
|
208
210
|
|
|
209
211
|
it('should throw an error if the cookie password is less than 32 characters', async () => {
|
|
210
212
|
const originalWorkosCookiePassword = envVariables.WORKOS_COOKIE_PASSWORD;
|
|
211
213
|
|
|
212
|
-
|
|
214
|
+
setEnvVar(envVariables, 'WORKOS_COOKIE_PASSWORD', 'short');
|
|
213
215
|
|
|
214
216
|
await expect(async () => {
|
|
215
217
|
await updateSessionMiddleware(
|
|
@@ -226,7 +228,7 @@ describe('session.ts', () => {
|
|
|
226
228
|
'You must provide a valid cookie password that is at least 32 characters in the environment variables.',
|
|
227
229
|
);
|
|
228
230
|
|
|
229
|
-
|
|
231
|
+
setEnvVar(envVariables, 'WORKOS_COOKIE_PASSWORD', originalWorkosCookiePassword);
|
|
230
232
|
});
|
|
231
233
|
|
|
232
234
|
it('should return early if there is no session', async () => {
|
|
@@ -247,7 +249,7 @@ describe('session.ts', () => {
|
|
|
247
249
|
});
|
|
248
250
|
|
|
249
251
|
it('should return 200 if the session is valid', async () => {
|
|
250
|
-
|
|
252
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
251
253
|
|
|
252
254
|
const nextCookies = await cookies();
|
|
253
255
|
nextCookies.set(
|
|
@@ -255,7 +257,7 @@ describe('session.ts', () => {
|
|
|
255
257
|
await sealData(mockSession, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
|
|
256
258
|
);
|
|
257
259
|
|
|
258
|
-
(jwtVerify as
|
|
260
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
259
261
|
return true;
|
|
260
262
|
});
|
|
261
263
|
|
|
@@ -278,11 +280,11 @@ describe('session.ts', () => {
|
|
|
278
280
|
it('should attempt to refresh the session when the access token is invalid', async () => {
|
|
279
281
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
280
282
|
|
|
281
|
-
(jwtVerify as
|
|
283
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
282
284
|
throw new Error('Invalid token');
|
|
283
285
|
});
|
|
284
286
|
|
|
285
|
-
|
|
287
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
286
288
|
accessToken: await generateTestToken(),
|
|
287
289
|
refreshToken: 'new-refresh-token',
|
|
288
290
|
user: mockSession.user,
|
|
@@ -314,17 +316,15 @@ describe('session.ts', () => {
|
|
|
314
316
|
});
|
|
315
317
|
|
|
316
318
|
it('should delete the cookie when refreshing fails', async () => {
|
|
317
|
-
|
|
319
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
318
320
|
|
|
319
321
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
320
322
|
|
|
321
|
-
(jwtVerify as
|
|
323
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
322
324
|
throw new Error('Invalid token');
|
|
323
325
|
});
|
|
324
326
|
|
|
325
|
-
|
|
326
|
-
.spyOn(workos.userManagement, 'authenticateWithRefreshToken')
|
|
327
|
-
.mockRejectedValue(new Error('Failed to refresh'));
|
|
327
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(new Error('Failed to refresh'));
|
|
328
328
|
|
|
329
329
|
const request = new NextRequest(new URL('http://example.com'));
|
|
330
330
|
|
|
@@ -360,7 +360,7 @@ describe('session.ts', () => {
|
|
|
360
360
|
|
|
361
361
|
describe('middleware auth', () => {
|
|
362
362
|
it('should redirect unauthenticated users on protected routes', async () => {
|
|
363
|
-
|
|
363
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
364
364
|
|
|
365
365
|
const request = new NextRequest(new URL('http://example.com/protected'));
|
|
366
366
|
const result = await updateSessionMiddleware(
|
|
@@ -380,10 +380,7 @@ describe('session.ts', () => {
|
|
|
380
380
|
);
|
|
381
381
|
});
|
|
382
382
|
|
|
383
|
-
it('should
|
|
384
|
-
const originalRedirect = NextResponse.redirect;
|
|
385
|
-
(NextResponse as Partial<typeof NextResponse>).redirect = undefined;
|
|
386
|
-
|
|
383
|
+
it('should return a redirect response when middlewareAuth is enabled and user is not authenticated', async () => {
|
|
387
384
|
const request = new NextRequest(new URL('http://example.com/protected'));
|
|
388
385
|
const result = await updateSessionMiddleware(
|
|
389
386
|
request,
|
|
@@ -396,10 +393,9 @@ describe('session.ts', () => {
|
|
|
396
393
|
[],
|
|
397
394
|
);
|
|
398
395
|
|
|
399
|
-
expect(result).toBeInstanceOf(
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
(NextResponse as Partial<typeof NextResponse>).redirect = originalRedirect;
|
|
396
|
+
expect(result).toBeInstanceOf(NextResponse);
|
|
397
|
+
expect(result.status).toBe(307);
|
|
398
|
+
expect(result.headers.get('Location')).toContain('workos.com');
|
|
403
399
|
});
|
|
404
400
|
|
|
405
401
|
it('should automatically add the redirect URI to unauthenticatedPaths when middleware is enabled', async () => {
|
|
@@ -435,7 +431,7 @@ describe('session.ts', () => {
|
|
|
435
431
|
expect(result.headers.get('Location')).toContain('screen_hint=sign-up');
|
|
436
432
|
});
|
|
437
433
|
|
|
438
|
-
it('should
|
|
434
|
+
it('should not leak sign-up paths header to the browser', async () => {
|
|
439
435
|
const request = new NextRequest(new URL('http://example.com/protected-signup'));
|
|
440
436
|
const result = await updateSessionMiddleware(
|
|
441
437
|
request,
|
|
@@ -448,7 +444,8 @@ describe('session.ts', () => {
|
|
|
448
444
|
['/protected-signup'],
|
|
449
445
|
);
|
|
450
446
|
|
|
451
|
-
|
|
447
|
+
// x-sign-up-paths is an internal header that should not leak to the browser
|
|
448
|
+
expect(result.headers.get('x-sign-up-paths')).toBeNull();
|
|
452
449
|
});
|
|
453
450
|
|
|
454
451
|
it('should allow logged out users on unauthenticated paths', async () => {
|
|
@@ -485,11 +482,11 @@ describe('session.ts', () => {
|
|
|
485
482
|
|
|
486
483
|
it('should throw an error if the provided regex is invalid and a non-Error object is thrown', async () => {
|
|
487
484
|
// Reset modules to ensure clean import state
|
|
488
|
-
|
|
485
|
+
vi.resetModules();
|
|
489
486
|
|
|
490
487
|
// Import first, then spy
|
|
491
488
|
const pathToRegexp = await import('path-to-regexp');
|
|
492
|
-
const parseSpy =
|
|
489
|
+
const parseSpy = vi.spyOn(pathToRegexp, 'parse').mockImplementation(() => {
|
|
493
490
|
throw 'invalid regex';
|
|
494
491
|
});
|
|
495
492
|
|
|
@@ -513,6 +510,9 @@ describe('session.ts', () => {
|
|
|
513
510
|
|
|
514
511
|
// Verify the mock was called
|
|
515
512
|
expect(parseSpy).toHaveBeenCalled();
|
|
513
|
+
|
|
514
|
+
// Restore the spy to prevent leaking to subsequent tests
|
|
515
|
+
parseSpy.mockRestore();
|
|
516
516
|
});
|
|
517
517
|
|
|
518
518
|
it('should default to the WORKOS_REDIRECT_URI environment variable if no redirect URI is provided', async () => {
|
|
@@ -532,17 +532,17 @@ describe('session.ts', () => {
|
|
|
532
532
|
});
|
|
533
533
|
|
|
534
534
|
it('should delete the cookie and redirect when refreshing fails', async () => {
|
|
535
|
-
|
|
535
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
536
536
|
|
|
537
537
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
538
538
|
|
|
539
|
-
(jwtVerify as
|
|
539
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
540
540
|
throw new Error('Invalid token');
|
|
541
541
|
});
|
|
542
542
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
543
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(
|
|
544
|
+
new Error('Failed to refresh'),
|
|
545
|
+
);
|
|
546
546
|
|
|
547
547
|
const request = new NextRequest(new URL('http://example.com'));
|
|
548
548
|
|
|
@@ -643,12 +643,12 @@ describe('session.ts', () => {
|
|
|
643
643
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
644
644
|
|
|
645
645
|
// Mock token verification to fail
|
|
646
|
-
(jwtVerify as
|
|
646
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
647
647
|
throw new Error('Invalid token');
|
|
648
648
|
});
|
|
649
649
|
|
|
650
650
|
// Mock successful refresh
|
|
651
|
-
|
|
651
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
652
652
|
accessToken: await generateTestToken(),
|
|
653
653
|
refreshToken: 'new-refresh-token',
|
|
654
654
|
user: mockSession.user,
|
|
@@ -676,12 +676,12 @@ describe('session.ts', () => {
|
|
|
676
676
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
677
677
|
|
|
678
678
|
// Mock token verification to fail
|
|
679
|
-
(jwtVerify as
|
|
679
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
680
680
|
throw new Error('Invalid token');
|
|
681
681
|
});
|
|
682
682
|
|
|
683
683
|
// Mock refresh failure
|
|
684
|
-
|
|
684
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(new Error('Refresh failed'));
|
|
685
685
|
|
|
686
686
|
const request = new NextRequest(new URL('http://example.com/protected'));
|
|
687
687
|
request.cookies.set(
|
|
@@ -703,15 +703,15 @@ describe('session.ts', () => {
|
|
|
703
703
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
704
704
|
|
|
705
705
|
// Mock token verification to fail
|
|
706
|
-
(jwtVerify as
|
|
706
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
707
707
|
throw new Error('Invalid token');
|
|
708
708
|
});
|
|
709
709
|
|
|
710
710
|
const newAccessToken = await generateTestToken();
|
|
711
|
-
const mockSuccessCallback =
|
|
711
|
+
const mockSuccessCallback = vi.fn();
|
|
712
712
|
|
|
713
713
|
// Mock successful refresh
|
|
714
|
-
|
|
714
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
715
715
|
accessToken: newAccessToken,
|
|
716
716
|
refreshToken: 'new-refresh-token',
|
|
717
717
|
user: mockSession.user,
|
|
@@ -742,15 +742,15 @@ describe('session.ts', () => {
|
|
|
742
742
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
743
743
|
|
|
744
744
|
// Mock token verification to fail
|
|
745
|
-
(jwtVerify as
|
|
745
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
746
746
|
throw new Error('Invalid token');
|
|
747
747
|
});
|
|
748
748
|
|
|
749
749
|
const mockError = new Error('Refresh failed');
|
|
750
|
-
const mockErrorCallback =
|
|
750
|
+
const mockErrorCallback = vi.fn();
|
|
751
751
|
|
|
752
752
|
// Mock refresh failure
|
|
753
|
-
|
|
753
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(mockError);
|
|
754
754
|
|
|
755
755
|
const request = new NextRequest(new URL('http://example.com/protected'));
|
|
756
756
|
request.cookies.set(
|
|
@@ -773,15 +773,15 @@ describe('session.ts', () => {
|
|
|
773
773
|
|
|
774
774
|
describe('refreshSession', () => {
|
|
775
775
|
it('should refresh session successfully', async () => {
|
|
776
|
-
|
|
776
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
777
777
|
accessToken: await generateTestToken(),
|
|
778
778
|
refreshToken: 'new-refresh-token',
|
|
779
779
|
user: mockSession.user,
|
|
780
780
|
});
|
|
781
781
|
|
|
782
|
-
|
|
783
|
-
.
|
|
784
|
-
|
|
782
|
+
vi.spyOn(workos.userManagement, 'getJwksUrl').mockReturnValue(
|
|
783
|
+
'https://api.workos.com/sso/jwks/client_1234567890',
|
|
784
|
+
);
|
|
785
785
|
|
|
786
786
|
const nextCookies = await cookies();
|
|
787
787
|
nextCookies.set(
|
|
@@ -817,15 +817,15 @@ describe('session.ts', () => {
|
|
|
817
817
|
await sealData(mockSession, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
|
|
818
818
|
);
|
|
819
819
|
|
|
820
|
-
|
|
820
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
821
821
|
accessToken: await generateTestToken({ org_id: 'org_456' }),
|
|
822
822
|
refreshToken: 'new-refresh-token',
|
|
823
823
|
user: mockSession.user,
|
|
824
824
|
});
|
|
825
825
|
|
|
826
|
-
|
|
827
|
-
.
|
|
828
|
-
|
|
826
|
+
vi.spyOn(workos.userManagement, 'getJwksUrl').mockReturnValue(
|
|
827
|
+
'https://api.workos.com/sso/jwks/client_1234567890',
|
|
828
|
+
);
|
|
829
829
|
|
|
830
830
|
const result = await refreshSession({ organizationId: 'org_456' });
|
|
831
831
|
|
|
@@ -844,8 +844,8 @@ describe('session.ts', () => {
|
|
|
844
844
|
'wos-session',
|
|
845
845
|
await sealData(mockSessionWithValidJWT, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
|
|
846
846
|
);
|
|
847
|
-
|
|
848
|
-
expect(refreshSession({ ensureSignedIn: false })).rejects.toThrow('Failed to refresh session: fail');
|
|
847
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue('fail');
|
|
848
|
+
await expect(refreshSession({ ensureSignedIn: false })).rejects.toThrow('Failed to refresh session: fail');
|
|
849
849
|
});
|
|
850
850
|
|
|
851
851
|
it('throws if authenticateWithRefreshToken fails with error', async () => {
|
|
@@ -859,7 +859,7 @@ describe('session.ts', () => {
|
|
|
859
859
|
'wos-session',
|
|
860
860
|
await sealData(mockSessionWithValidJWT, { password: process.env.WORKOS_COOKIE_PASSWORD as string }),
|
|
861
861
|
);
|
|
862
|
-
|
|
862
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(new Error('error'));
|
|
863
863
|
await expect(refreshSession()).rejects.toThrow('Failed to refresh session: error');
|
|
864
864
|
});
|
|
865
865
|
});
|
|
@@ -869,7 +869,7 @@ describe('session.ts', () => {
|
|
|
869
869
|
const nextCookies = await cookies();
|
|
870
870
|
// @ts-expect-error - _reset is part of the mock
|
|
871
871
|
nextCookies._reset();
|
|
872
|
-
|
|
872
|
+
vi.clearAllMocks();
|
|
873
873
|
});
|
|
874
874
|
|
|
875
875
|
it('should return all token claims when accessToken is provided', async () => {
|
|
@@ -963,7 +963,7 @@ describe('session.ts', () => {
|
|
|
963
963
|
|
|
964
964
|
describe('eager auth functionality', () => {
|
|
965
965
|
beforeEach(() => {
|
|
966
|
-
|
|
966
|
+
vi.clearAllMocks();
|
|
967
967
|
});
|
|
968
968
|
|
|
969
969
|
describe('isInitialDocumentRequest', () => {
|
|
@@ -1057,12 +1057,12 @@ describe('session.ts', () => {
|
|
|
1057
1057
|
// Setup invalid session that needs refresh
|
|
1058
1058
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
1059
1059
|
|
|
1060
|
-
(jwtVerify as
|
|
1060
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
1061
1061
|
throw new Error('Invalid token');
|
|
1062
1062
|
});
|
|
1063
1063
|
|
|
1064
1064
|
const newAccessToken = await generateTestToken();
|
|
1065
|
-
|
|
1065
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockResolvedValue({
|
|
1066
1066
|
accessToken: newAccessToken,
|
|
1067
1067
|
refreshToken: 'new-refresh-token',
|
|
1068
1068
|
user: mockSession.user,
|
|
@@ -1088,13 +1088,11 @@ describe('session.ts', () => {
|
|
|
1088
1088
|
// Setup invalid session
|
|
1089
1089
|
mockSession.accessToken = await generateTestToken({}, true);
|
|
1090
1090
|
|
|
1091
|
-
(jwtVerify as
|
|
1091
|
+
(jwtVerify as Mock).mockImplementation(() => {
|
|
1092
1092
|
throw new Error('Invalid token');
|
|
1093
1093
|
});
|
|
1094
1094
|
|
|
1095
|
-
|
|
1096
|
-
.spyOn(workos.userManagement, 'authenticateWithRefreshToken')
|
|
1097
|
-
.mockRejectedValue(new Error('Refresh failed'));
|
|
1095
|
+
vi.spyOn(workos.userManagement, 'authenticateWithRefreshToken').mockRejectedValue(new Error('Refresh failed'));
|
|
1098
1096
|
|
|
1099
1097
|
const request = new NextRequest(new URL('http://example.com/page'));
|
|
1100
1098
|
request.headers.set('accept', 'text/html');
|