@dotpress/auth 1.3.2 → 2.0.0-alpha.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/LICENSE +1 -1
- package/README.md +355 -329
- package/dist/index.d.ts +679 -8
- package/dist/index.js +1226 -20
- package/dist/index.js.map +1 -0
- package/package.json +32 -46
- package/dist/__tests__/functional/cookies.test.d.ts +0 -1
- package/dist/__tests__/functional/cookies.test.js +0 -621
- package/dist/__tests__/functional/handlers.test.d.ts +0 -1
- package/dist/__tests__/functional/handlers.test.js +0 -239
- package/dist/__tests__/functional/middlewares.test.d.ts +0 -1
- package/dist/__tests__/functional/middlewares.test.js +0 -111
- package/dist/__tests__/unit/cookies.test.d.ts +0 -1
- package/dist/__tests__/unit/cookies.test.js +0 -466
- package/dist/__tests__/unit/tokens.test.d.ts +0 -1
- package/dist/__tests__/unit/tokens.test.js +0 -225
- package/dist/lib/cookies.d.ts +0 -42
- package/dist/lib/cookies.js +0 -136
- package/dist/lib/generators.d.ts +0 -1
- package/dist/lib/generators.js +0 -4
- package/dist/lib/handlers.d.ts +0 -2
- package/dist/lib/handlers.js +0 -263
- package/dist/lib/ip.d.ts +0 -2
- package/dist/lib/ip.js +0 -15
- package/dist/lib/middleware.d.ts +0 -2
- package/dist/lib/middleware.js +0 -23
- package/dist/lib/settings.d.ts +0 -3
- package/dist/lib/settings.js +0 -42
- package/dist/lib/tokens.d.ts +0 -38
- package/dist/lib/tokens.js +0 -112
- package/dist/types/types.d.ts +0 -107
- package/dist/types/types.js +0 -1
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import request from 'supertest';
|
|
3
|
-
import bcrypt from 'bcryptjs';
|
|
4
|
-
import { $clearRoutes, createApp } from 'dotpress';
|
|
5
|
-
import * as tokenFns from '../../lib/tokens.js';
|
|
6
|
-
import { configurePlugin } from '../../index.js';
|
|
7
|
-
describe('Cookie Mode Authentication', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
vi.resetAllMocks();
|
|
10
|
-
$clearRoutes();
|
|
11
|
-
});
|
|
12
|
-
const dataProvider = {
|
|
13
|
-
storeRefreshToken: vi.fn(),
|
|
14
|
-
findRefreshToken: vi.fn(),
|
|
15
|
-
markRefreshTokenAsUsed: vi.fn(),
|
|
16
|
-
revokeTokens: vi.fn(),
|
|
17
|
-
findUserById: vi.fn(),
|
|
18
|
-
findUserIdentifiers: vi.fn(),
|
|
19
|
-
};
|
|
20
|
-
const authPlugin = configurePlugin({
|
|
21
|
-
keys: {
|
|
22
|
-
secretKey: '',
|
|
23
|
-
publicKey: '',
|
|
24
|
-
},
|
|
25
|
-
dataProvider,
|
|
26
|
-
cookies: {
|
|
27
|
-
enabled: true,
|
|
28
|
-
secure: false, // For testing
|
|
29
|
-
httpOnly: true,
|
|
30
|
-
sameSite: 'lax',
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
describe('POST /auth/login with cookie mode', () => {
|
|
34
|
-
it('should set cookies when mode is "cookie"', async () => {
|
|
35
|
-
const pwdHash = await bcrypt.hash('123456', 10);
|
|
36
|
-
vi.mocked(dataProvider.findUserIdentifiers).mockResolvedValue({
|
|
37
|
-
userId: '101',
|
|
38
|
-
passwordHash: pwdHash,
|
|
39
|
-
});
|
|
40
|
-
vi.spyOn(tokenFns, 'generateTokensForUser').mockResolvedValue({
|
|
41
|
-
accessToken: 'acc-111222333',
|
|
42
|
-
refreshToken: 'ref-999888777',
|
|
43
|
-
});
|
|
44
|
-
const app = await createApp({
|
|
45
|
-
plugins: [authPlugin],
|
|
46
|
-
});
|
|
47
|
-
const res = await request(app).post('/auth/login').send({
|
|
48
|
-
username: 'JaneDoe',
|
|
49
|
-
password: '123456',
|
|
50
|
-
mode: 'cookie',
|
|
51
|
-
});
|
|
52
|
-
expect(res.status).toBe(200);
|
|
53
|
-
// Check response body
|
|
54
|
-
expect(res.body).toMatchObject({
|
|
55
|
-
userId: '101',
|
|
56
|
-
accessToken: 'acc-111222333',
|
|
57
|
-
refreshToken: 'ref-999888777',
|
|
58
|
-
expiresIn: expect.any(Number),
|
|
59
|
-
expiresAt: expect.any(String),
|
|
60
|
-
});
|
|
61
|
-
// Check cookies are set
|
|
62
|
-
expect(res.headers['set-cookie']).toBeDefined();
|
|
63
|
-
const cookies = Array.isArray(res.headers['set-cookie'])
|
|
64
|
-
? res.headers['set-cookie']
|
|
65
|
-
: [res.headers['set-cookie']];
|
|
66
|
-
// Check refresh token cookie
|
|
67
|
-
const refreshCookie = cookies.find((c) => c.startsWith('dotpress_refresh_token='));
|
|
68
|
-
expect(refreshCookie).toBeDefined();
|
|
69
|
-
expect(refreshCookie).toContain('HttpOnly');
|
|
70
|
-
expect(refreshCookie).toContain('Path=/');
|
|
71
|
-
expect(refreshCookie).toContain('SameSite=Lax');
|
|
72
|
-
// Access token should NOT be in cookie by default
|
|
73
|
-
const accessCookie = cookies.find((c) => c.startsWith('dotpress_access_token='));
|
|
74
|
-
expect(accessCookie).toBeUndefined();
|
|
75
|
-
});
|
|
76
|
-
it('should set access token cookie when accessTokenInCookie is true', async () => {
|
|
77
|
-
const pwdHash = await bcrypt.hash('123456', 10);
|
|
78
|
-
vi.mocked(dataProvider.findUserIdentifiers).mockResolvedValue({
|
|
79
|
-
userId: '101',
|
|
80
|
-
passwordHash: pwdHash,
|
|
81
|
-
});
|
|
82
|
-
vi.spyOn(tokenFns, 'generateTokensForUser').mockResolvedValue({
|
|
83
|
-
accessToken: 'acc-111222333',
|
|
84
|
-
refreshToken: 'ref-999888777',
|
|
85
|
-
});
|
|
86
|
-
const authPluginWithAccessCookie = configurePlugin({
|
|
87
|
-
keys: {
|
|
88
|
-
secretKey: '',
|
|
89
|
-
publicKey: '',
|
|
90
|
-
},
|
|
91
|
-
dataProvider,
|
|
92
|
-
cookies: {
|
|
93
|
-
enabled: true,
|
|
94
|
-
secure: false,
|
|
95
|
-
accessTokenInCookie: true, // Enable access token in cookie
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
const app = await createApp({
|
|
99
|
-
plugins: [authPluginWithAccessCookie],
|
|
100
|
-
});
|
|
101
|
-
const res = await request(app).post('/auth/login').send({
|
|
102
|
-
username: 'JaneDoe',
|
|
103
|
-
password: '123456',
|
|
104
|
-
mode: 'cookie',
|
|
105
|
-
});
|
|
106
|
-
expect(res.status).toBe(200);
|
|
107
|
-
const cookies = Array.isArray(res.headers['set-cookie'])
|
|
108
|
-
? res.headers['set-cookie']
|
|
109
|
-
: [res.headers['set-cookie']];
|
|
110
|
-
// Both tokens should be in cookies
|
|
111
|
-
const refreshCookie = cookies.find((c) => c.startsWith('dotpress_refresh_token='));
|
|
112
|
-
const accessCookie = cookies.find((c) => c.startsWith('dotpress_access_token='));
|
|
113
|
-
expect(refreshCookie).toBeDefined();
|
|
114
|
-
expect(accessCookie).toBeDefined();
|
|
115
|
-
});
|
|
116
|
-
it('should NOT set cookies when mode is "body"', async () => {
|
|
117
|
-
const pwdHash = await bcrypt.hash('123456', 10);
|
|
118
|
-
vi.mocked(dataProvider.findUserIdentifiers).mockResolvedValue({
|
|
119
|
-
userId: '101',
|
|
120
|
-
passwordHash: pwdHash,
|
|
121
|
-
});
|
|
122
|
-
vi.spyOn(tokenFns, 'generateTokensForUser').mockResolvedValue({
|
|
123
|
-
accessToken: 'acc-111222333',
|
|
124
|
-
refreshToken: 'ref-999888777',
|
|
125
|
-
});
|
|
126
|
-
const app = await createApp({
|
|
127
|
-
plugins: [authPlugin],
|
|
128
|
-
});
|
|
129
|
-
const res = await request(app).post('/auth/login').send({
|
|
130
|
-
username: 'JaneDoe',
|
|
131
|
-
password: '123456',
|
|
132
|
-
mode: 'body',
|
|
133
|
-
});
|
|
134
|
-
expect(res.status).toBe(200);
|
|
135
|
-
expect(res.body.accessToken).toBe('acc-111222333');
|
|
136
|
-
// No cookies should be set
|
|
137
|
-
expect(res.headers['set-cookie']).toBeUndefined();
|
|
138
|
-
});
|
|
139
|
-
it('should NOT set cookies when mode is not specified (default)', async () => {
|
|
140
|
-
const pwdHash = await bcrypt.hash('123456', 10);
|
|
141
|
-
vi.mocked(dataProvider.findUserIdentifiers).mockResolvedValue({
|
|
142
|
-
userId: '101',
|
|
143
|
-
passwordHash: pwdHash,
|
|
144
|
-
});
|
|
145
|
-
vi.spyOn(tokenFns, 'generateTokensForUser').mockResolvedValue({
|
|
146
|
-
accessToken: 'acc-111222333',
|
|
147
|
-
refreshToken: 'ref-999888777',
|
|
148
|
-
});
|
|
149
|
-
const app = await createApp({
|
|
150
|
-
plugins: [authPlugin],
|
|
151
|
-
});
|
|
152
|
-
const res = await request(app).post('/auth/login').send({
|
|
153
|
-
username: 'JaneDoe',
|
|
154
|
-
password: '123456',
|
|
155
|
-
});
|
|
156
|
-
expect(res.status).toBe(200);
|
|
157
|
-
// No cookies should be set (backward compatibility)
|
|
158
|
-
expect(res.headers['set-cookie']).toBeUndefined();
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
describe('POST /auth/refresh with cookies', () => {
|
|
162
|
-
it('should refresh tokens using cookies', async () => {
|
|
163
|
-
vi.spyOn(tokenFns, 'generateTokensFromRefreshToken').mockResolvedValue({
|
|
164
|
-
userId: '102',
|
|
165
|
-
accessToken: 'new-acc-token',
|
|
166
|
-
refreshToken: 'new-ref-token',
|
|
167
|
-
});
|
|
168
|
-
const app = await createApp({
|
|
169
|
-
plugins: [authPlugin],
|
|
170
|
-
});
|
|
171
|
-
// Simulate request with cookies
|
|
172
|
-
const res = await request(app)
|
|
173
|
-
.post('/auth/refresh')
|
|
174
|
-
.set('Cookie', [
|
|
175
|
-
'dotpress_refresh_token=ref-999888777',
|
|
176
|
-
'dotpress_access_token=acc-111222333',
|
|
177
|
-
])
|
|
178
|
-
.send({});
|
|
179
|
-
expect(res.status).toBe(200);
|
|
180
|
-
expect(res.body).toMatchObject({
|
|
181
|
-
userId: '102',
|
|
182
|
-
accessToken: 'new-acc-token',
|
|
183
|
-
refreshToken: 'new-ref-token',
|
|
184
|
-
expiresIn: expect.any(Number),
|
|
185
|
-
expiresAt: expect.any(String),
|
|
186
|
-
});
|
|
187
|
-
// New cookies should be set
|
|
188
|
-
expect(res.headers['set-cookie']).toBeDefined();
|
|
189
|
-
const cookies = Array.isArray(res.headers['set-cookie'])
|
|
190
|
-
? res.headers['set-cookie']
|
|
191
|
-
: [res.headers['set-cookie']];
|
|
192
|
-
expect(cookies.some((c) => c.includes('new-ref-token'))).toBe(true);
|
|
193
|
-
});
|
|
194
|
-
it('should fallback to body mode when no cookies present', async () => {
|
|
195
|
-
vi.spyOn(tokenFns, 'generateTokensFromRefreshToken').mockResolvedValue({
|
|
196
|
-
userId: '102',
|
|
197
|
-
accessToken: 'new-acc-token',
|
|
198
|
-
refreshToken: 'new-ref-token',
|
|
199
|
-
});
|
|
200
|
-
const app = await createApp({
|
|
201
|
-
plugins: [authPlugin],
|
|
202
|
-
});
|
|
203
|
-
const res = await request(app).post('/auth/refresh').send({
|
|
204
|
-
accessToken: 'acc-111222333',
|
|
205
|
-
refreshToken: 'ref-999888777',
|
|
206
|
-
});
|
|
207
|
-
expect(res.status).toBe(200);
|
|
208
|
-
expect(res.body.accessToken).toBe('new-acc-token');
|
|
209
|
-
// No cookies should be set in body mode
|
|
210
|
-
expect(res.headers['set-cookie']).toBeUndefined();
|
|
211
|
-
});
|
|
212
|
-
it('should return 401 when no tokens provided', async () => {
|
|
213
|
-
const app = await createApp({
|
|
214
|
-
plugins: [authPlugin],
|
|
215
|
-
});
|
|
216
|
-
const res = await request(app).post('/auth/refresh').send({});
|
|
217
|
-
expect(res.status).toBe(401);
|
|
218
|
-
expect(res.body.error).toBe('UNAUTHORIZED');
|
|
219
|
-
expect(res.body.message).toBe('NO_TOKENS_PROVIDED');
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
describe('POST /auth/logout with cookies', () => {
|
|
223
|
-
it('should clear cookies on logout', async () => {
|
|
224
|
-
vi.mocked(dataProvider.findUserById).mockResolvedValue({
|
|
225
|
-
id: 'usr-123',
|
|
226
|
-
username: 'JohnDoe',
|
|
227
|
-
email: 'john@example.com',
|
|
228
|
-
});
|
|
229
|
-
vi.spyOn(tokenFns, 'validateAndDecodeAccessToken').mockReturnValue({
|
|
230
|
-
userId: 'usr-123',
|
|
231
|
-
});
|
|
232
|
-
const app = await createApp({
|
|
233
|
-
plugins: [authPlugin],
|
|
234
|
-
});
|
|
235
|
-
const res = await request(app)
|
|
236
|
-
.post('/auth/logout')
|
|
237
|
-
.set('Authorization', 'Bearer valid-token')
|
|
238
|
-
.set('Cookie', ['dotpress_refresh_token=ref-token']);
|
|
239
|
-
expect(res.status).toBe(200);
|
|
240
|
-
expect(res.body).toEqual({ success: true });
|
|
241
|
-
// Cookies should be cleared
|
|
242
|
-
expect(res.headers['set-cookie']).toBeDefined();
|
|
243
|
-
const cookies = Array.isArray(res.headers['set-cookie'])
|
|
244
|
-
? res.headers['set-cookie']
|
|
245
|
-
: [res.headers['set-cookie']];
|
|
246
|
-
// Check that cookies are being cleared (Max-Age=0 or Expires in the past)
|
|
247
|
-
const refreshCookie = cookies.find((c) => c.startsWith('dotpress_refresh_token='));
|
|
248
|
-
expect(refreshCookie).toBeDefined();
|
|
249
|
-
// Cookie should have an expiry in the past or Max-Age=0
|
|
250
|
-
expect(refreshCookie.includes('Max-Age=0') ||
|
|
251
|
-
refreshCookie.includes('Expires=Thu, 01 Jan 1970')).toBe(true);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
describe('GET /auth/me with cookies', () => {
|
|
255
|
-
it('should authenticate user with access token from Authorization header', async () => {
|
|
256
|
-
vi.mocked(dataProvider.findUserById).mockResolvedValue({
|
|
257
|
-
id: 'usr-123',
|
|
258
|
-
username: 'JohnDoe',
|
|
259
|
-
email: 'john@example.com',
|
|
260
|
-
});
|
|
261
|
-
vi.spyOn(tokenFns, 'validateAndDecodeAccessToken').mockReturnValue({
|
|
262
|
-
userId: 'usr-123',
|
|
263
|
-
});
|
|
264
|
-
const app = await createApp({
|
|
265
|
-
plugins: [authPlugin],
|
|
266
|
-
});
|
|
267
|
-
const res = await request(app)
|
|
268
|
-
.get('/auth/me')
|
|
269
|
-
.set('Authorization', 'Bearer valid-token');
|
|
270
|
-
expect(res.status).toBe(200);
|
|
271
|
-
expect(res.body).toEqual({
|
|
272
|
-
id: 'usr-123',
|
|
273
|
-
username: 'JohnDoe',
|
|
274
|
-
email: 'john@example.com',
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
it('should authenticate user with access token from cookie when accessTokenInCookie is true', async () => {
|
|
278
|
-
vi.mocked(dataProvider.findUserById).mockResolvedValue({
|
|
279
|
-
id: 'usr-456',
|
|
280
|
-
username: 'JaneDoe',
|
|
281
|
-
email: 'jane@example.com',
|
|
282
|
-
});
|
|
283
|
-
vi.spyOn(tokenFns, 'validateAndDecodeAccessToken').mockReturnValue({
|
|
284
|
-
userId: 'usr-456',
|
|
285
|
-
});
|
|
286
|
-
const authPluginWithAccessCookie = configurePlugin({
|
|
287
|
-
keys: {
|
|
288
|
-
secretKey: '',
|
|
289
|
-
publicKey: '',
|
|
290
|
-
},
|
|
291
|
-
dataProvider,
|
|
292
|
-
cookies: {
|
|
293
|
-
enabled: true,
|
|
294
|
-
secure: false,
|
|
295
|
-
accessTokenInCookie: true,
|
|
296
|
-
},
|
|
297
|
-
});
|
|
298
|
-
const app = await createApp({
|
|
299
|
-
plugins: [authPluginWithAccessCookie],
|
|
300
|
-
});
|
|
301
|
-
const res = await request(app)
|
|
302
|
-
.get('/auth/me')
|
|
303
|
-
.set('Cookie', ['dotpress_access_token=cookie-token']);
|
|
304
|
-
expect(res.status).toBe(200);
|
|
305
|
-
expect(res.body.username).toBe('JaneDoe');
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
describe('Custom cookie names', () => {
|
|
309
|
-
it('should use custom cookie names when configured', async () => {
|
|
310
|
-
const pwdHash = await bcrypt.hash('123456', 10);
|
|
311
|
-
vi.mocked(dataProvider.findUserIdentifiers).mockResolvedValue({
|
|
312
|
-
userId: '101',
|
|
313
|
-
passwordHash: pwdHash,
|
|
314
|
-
});
|
|
315
|
-
vi.spyOn(tokenFns, 'generateTokensForUser').mockResolvedValue({
|
|
316
|
-
accessToken: 'acc-token',
|
|
317
|
-
refreshToken: 'ref-token',
|
|
318
|
-
});
|
|
319
|
-
const customAuthPlugin = configurePlugin({
|
|
320
|
-
keys: {
|
|
321
|
-
secretKey: '',
|
|
322
|
-
publicKey: '',
|
|
323
|
-
},
|
|
324
|
-
dataProvider,
|
|
325
|
-
cookies: {
|
|
326
|
-
enabled: true,
|
|
327
|
-
secure: false,
|
|
328
|
-
accessTokenName: 'custom_access',
|
|
329
|
-
refreshTokenName: 'custom_refresh',
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
const app = await createApp({
|
|
333
|
-
plugins: [customAuthPlugin],
|
|
334
|
-
});
|
|
335
|
-
const res = await request(app).post('/auth/login').send({
|
|
336
|
-
username: 'JaneDoe',
|
|
337
|
-
password: '123456',
|
|
338
|
-
mode: 'cookie',
|
|
339
|
-
});
|
|
340
|
-
expect(res.status).toBe(200);
|
|
341
|
-
const cookies = Array.isArray(res.headers['set-cookie'])
|
|
342
|
-
? res.headers['set-cookie']
|
|
343
|
-
: [res.headers['set-cookie']];
|
|
344
|
-
expect(cookies.some((c) => c.startsWith('custom_refresh='))).toBe(true);
|
|
345
|
-
});
|
|
346
|
-
});
|
|
347
|
-
describe('Cross-domain cookie configuration', () => {
|
|
348
|
-
it('should set domain attribute when configured', async () => {
|
|
349
|
-
const pwdHash = await bcrypt.hash('123456', 10);
|
|
350
|
-
vi.mocked(dataProvider.findUserIdentifiers).mockResolvedValue({
|
|
351
|
-
userId: '101',
|
|
352
|
-
passwordHash: pwdHash,
|
|
353
|
-
});
|
|
354
|
-
vi.spyOn(tokenFns, 'generateTokensForUser').mockResolvedValue({
|
|
355
|
-
accessToken: 'acc-token',
|
|
356
|
-
refreshToken: 'ref-token',
|
|
357
|
-
});
|
|
358
|
-
const domainAuthPlugin = configurePlugin({
|
|
359
|
-
keys: {
|
|
360
|
-
secretKey: '',
|
|
361
|
-
publicKey: '',
|
|
362
|
-
},
|
|
363
|
-
dataProvider,
|
|
364
|
-
cookies: {
|
|
365
|
-
enabled: true,
|
|
366
|
-
secure: false,
|
|
367
|
-
domain: '.example.com',
|
|
368
|
-
},
|
|
369
|
-
});
|
|
370
|
-
const app = await createApp({
|
|
371
|
-
plugins: [domainAuthPlugin],
|
|
372
|
-
});
|
|
373
|
-
const res = await request(app).post('/auth/login').send({
|
|
374
|
-
username: 'JaneDoe',
|
|
375
|
-
password: '123456',
|
|
376
|
-
mode: 'cookie',
|
|
377
|
-
});
|
|
378
|
-
expect(res.status).toBe(200);
|
|
379
|
-
const cookies = Array.isArray(res.headers['set-cookie'])
|
|
380
|
-
? res.headers['set-cookie']
|
|
381
|
-
: [res.headers['set-cookie']];
|
|
382
|
-
const refreshCookie = cookies.find((c) => c.startsWith('dotpress_refresh_token='));
|
|
383
|
-
expect(refreshCookie).toContain('Domain=.example.com');
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
describe('POST /auth/restore', () => {
|
|
387
|
-
it('should return 401 when cookies are not enabled', async () => {
|
|
388
|
-
const authPluginNoCookies = configurePlugin({
|
|
389
|
-
keys: {
|
|
390
|
-
secretKey: '',
|
|
391
|
-
publicKey: '',
|
|
392
|
-
},
|
|
393
|
-
dataProvider,
|
|
394
|
-
cookies: {
|
|
395
|
-
enabled: false,
|
|
396
|
-
},
|
|
397
|
-
});
|
|
398
|
-
const app = await createApp({
|
|
399
|
-
plugins: [authPluginNoCookies],
|
|
400
|
-
});
|
|
401
|
-
const res = await request(app).post('/auth/restore');
|
|
402
|
-
expect(res.status).toBe(401);
|
|
403
|
-
});
|
|
404
|
-
it('should return 401 when accessTokenInCookie is not enabled', async () => {
|
|
405
|
-
const authPluginNoAccessCookie = configurePlugin({
|
|
406
|
-
keys: {
|
|
407
|
-
secretKey: '',
|
|
408
|
-
publicKey: '',
|
|
409
|
-
},
|
|
410
|
-
dataProvider,
|
|
411
|
-
cookies: {
|
|
412
|
-
enabled: true,
|
|
413
|
-
accessTokenInCookie: false, // Required for restore
|
|
414
|
-
},
|
|
415
|
-
});
|
|
416
|
-
const app = await createApp({
|
|
417
|
-
plugins: [authPluginNoAccessCookie],
|
|
418
|
-
});
|
|
419
|
-
const res = await request(app)
|
|
420
|
-
.post('/auth/restore')
|
|
421
|
-
.set('Cookie', ['dotpress_refresh_token=ref-token']);
|
|
422
|
-
expect(res.status).toBe(401);
|
|
423
|
-
});
|
|
424
|
-
it('should return 401 when refresh token is missing', async () => {
|
|
425
|
-
const authPluginWithAccessCookie = configurePlugin({
|
|
426
|
-
keys: {
|
|
427
|
-
secretKey: '',
|
|
428
|
-
publicKey: '',
|
|
429
|
-
},
|
|
430
|
-
dataProvider,
|
|
431
|
-
cookies: {
|
|
432
|
-
enabled: true,
|
|
433
|
-
accessTokenInCookie: true,
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
const app = await createApp({
|
|
437
|
-
plugins: [authPluginWithAccessCookie],
|
|
438
|
-
});
|
|
439
|
-
const res = await request(app)
|
|
440
|
-
.post('/auth/restore')
|
|
441
|
-
.set('Cookie', ['dotpress_access_token=acc-token']);
|
|
442
|
-
expect(res.status).toBe(401);
|
|
443
|
-
});
|
|
444
|
-
it('should return 401 when access token is missing', async () => {
|
|
445
|
-
const authPluginWithAccessCookie = configurePlugin({
|
|
446
|
-
keys: {
|
|
447
|
-
secretKey: '',
|
|
448
|
-
publicKey: '',
|
|
449
|
-
},
|
|
450
|
-
dataProvider,
|
|
451
|
-
cookies: {
|
|
452
|
-
enabled: true,
|
|
453
|
-
accessTokenInCookie: true,
|
|
454
|
-
},
|
|
455
|
-
});
|
|
456
|
-
const app = await createApp({
|
|
457
|
-
plugins: [authPluginWithAccessCookie],
|
|
458
|
-
});
|
|
459
|
-
const res = await request(app)
|
|
460
|
-
.post('/auth/restore')
|
|
461
|
-
.set('Cookie', ['dotpress_refresh_token=ref-token']);
|
|
462
|
-
expect(res.status).toBe(401);
|
|
463
|
-
});
|
|
464
|
-
it('should return user and tokens when access token is still valid', async () => {
|
|
465
|
-
vi.spyOn(tokenFns, 'validateAndDecodeAccessToken').mockReturnValue({
|
|
466
|
-
userId: 'usr-123',
|
|
467
|
-
});
|
|
468
|
-
vi.mocked(dataProvider.findUserById).mockResolvedValue({
|
|
469
|
-
id: 'usr-123',
|
|
470
|
-
username: 'JohnDoe',
|
|
471
|
-
email: 'john@example.com',
|
|
472
|
-
});
|
|
473
|
-
const authPluginWithAccessCookie = configurePlugin({
|
|
474
|
-
keys: {
|
|
475
|
-
secretKey: '',
|
|
476
|
-
publicKey: '',
|
|
477
|
-
},
|
|
478
|
-
dataProvider,
|
|
479
|
-
cookies: {
|
|
480
|
-
enabled: true,
|
|
481
|
-
accessTokenInCookie: true,
|
|
482
|
-
},
|
|
483
|
-
});
|
|
484
|
-
const app = await createApp({
|
|
485
|
-
plugins: [authPluginWithAccessCookie],
|
|
486
|
-
});
|
|
487
|
-
const res = await request(app)
|
|
488
|
-
.post('/auth/restore')
|
|
489
|
-
.set('Cookie', [
|
|
490
|
-
'dotpress_access_token=valid-access-token',
|
|
491
|
-
'dotpress_refresh_token=valid-refresh-token',
|
|
492
|
-
]);
|
|
493
|
-
expect(res.status).toBe(200);
|
|
494
|
-
expect(res.body).toMatchObject({
|
|
495
|
-
userId: 'usr-123',
|
|
496
|
-
accessToken: 'valid-access-token',
|
|
497
|
-
refreshToken: 'valid-refresh-token',
|
|
498
|
-
expiresIn: expect.any(Number),
|
|
499
|
-
expiresAt: expect.any(String),
|
|
500
|
-
user: {
|
|
501
|
-
id: 'usr-123',
|
|
502
|
-
username: 'JohnDoe',
|
|
503
|
-
email: 'john@example.com',
|
|
504
|
-
},
|
|
505
|
-
});
|
|
506
|
-
});
|
|
507
|
-
it('should refresh tokens and return new tokens when access token is expired', async () => {
|
|
508
|
-
// First call: validateAndDecodeAccessToken throws (expired)
|
|
509
|
-
vi.spyOn(tokenFns, 'validateAndDecodeAccessToken').mockImplementation(() => {
|
|
510
|
-
throw new Error('Token expired');
|
|
511
|
-
});
|
|
512
|
-
// Second call: generateTokensFromRefreshToken returns new tokens
|
|
513
|
-
vi.spyOn(tokenFns, 'generateTokensFromRefreshToken').mockResolvedValue({
|
|
514
|
-
userId: 'usr-456',
|
|
515
|
-
accessToken: 'new-access-token',
|
|
516
|
-
refreshToken: 'new-refresh-token',
|
|
517
|
-
});
|
|
518
|
-
vi.mocked(dataProvider.findUserById).mockResolvedValue({
|
|
519
|
-
id: 'usr-456',
|
|
520
|
-
username: 'JaneDoe',
|
|
521
|
-
email: 'jane@example.com',
|
|
522
|
-
});
|
|
523
|
-
const authPluginWithAccessCookie = configurePlugin({
|
|
524
|
-
keys: {
|
|
525
|
-
secretKey: '',
|
|
526
|
-
publicKey: '',
|
|
527
|
-
},
|
|
528
|
-
dataProvider,
|
|
529
|
-
cookies: {
|
|
530
|
-
enabled: true,
|
|
531
|
-
accessTokenInCookie: true,
|
|
532
|
-
},
|
|
533
|
-
});
|
|
534
|
-
const app = await createApp({
|
|
535
|
-
plugins: [authPluginWithAccessCookie],
|
|
536
|
-
});
|
|
537
|
-
const res = await request(app)
|
|
538
|
-
.post('/auth/restore')
|
|
539
|
-
.set('Cookie', [
|
|
540
|
-
'dotpress_access_token=expired-access-token',
|
|
541
|
-
'dotpress_refresh_token=valid-refresh-token',
|
|
542
|
-
]);
|
|
543
|
-
expect(res.status).toBe(200);
|
|
544
|
-
expect(res.body).toMatchObject({
|
|
545
|
-
userId: 'usr-456',
|
|
546
|
-
accessToken: 'new-access-token',
|
|
547
|
-
refreshToken: 'new-refresh-token',
|
|
548
|
-
expiresIn: expect.any(Number),
|
|
549
|
-
expiresAt: expect.any(String),
|
|
550
|
-
user: {
|
|
551
|
-
id: 'usr-456',
|
|
552
|
-
username: 'JaneDoe',
|
|
553
|
-
email: 'jane@example.com',
|
|
554
|
-
},
|
|
555
|
-
});
|
|
556
|
-
// Verify new cookies were set
|
|
557
|
-
expect(res.headers['set-cookie']).toBeDefined();
|
|
558
|
-
const cookies = Array.isArray(res.headers['set-cookie'])
|
|
559
|
-
? res.headers['set-cookie']
|
|
560
|
-
: [res.headers['set-cookie']];
|
|
561
|
-
const accessCookie = cookies.find((c) => c.startsWith('dotpress_access_token='));
|
|
562
|
-
const refreshCookie = cookies.find((c) => c.startsWith('dotpress_refresh_token='));
|
|
563
|
-
expect(accessCookie).toBeDefined();
|
|
564
|
-
expect(refreshCookie).toBeDefined();
|
|
565
|
-
});
|
|
566
|
-
it('should return 401 when refresh token is invalid', async () => {
|
|
567
|
-
vi.spyOn(tokenFns, 'validateAndDecodeAccessToken').mockImplementation(() => {
|
|
568
|
-
throw new Error('Token expired');
|
|
569
|
-
});
|
|
570
|
-
vi.spyOn(tokenFns, 'generateTokensFromRefreshToken').mockRejectedValue(new Error('Invalid_Refresh_Token'));
|
|
571
|
-
const authPluginWithAccessCookie = configurePlugin({
|
|
572
|
-
keys: {
|
|
573
|
-
secretKey: '',
|
|
574
|
-
publicKey: '',
|
|
575
|
-
},
|
|
576
|
-
dataProvider,
|
|
577
|
-
cookies: {
|
|
578
|
-
enabled: true,
|
|
579
|
-
accessTokenInCookie: true,
|
|
580
|
-
},
|
|
581
|
-
});
|
|
582
|
-
const app = await createApp({
|
|
583
|
-
plugins: [authPluginWithAccessCookie],
|
|
584
|
-
});
|
|
585
|
-
const res = await request(app)
|
|
586
|
-
.post('/auth/restore')
|
|
587
|
-
.set('Cookie', [
|
|
588
|
-
'dotpress_access_token=expired-access-token',
|
|
589
|
-
'dotpress_refresh_token=invalid-refresh-token',
|
|
590
|
-
]);
|
|
591
|
-
expect(res.status).toBe(401);
|
|
592
|
-
});
|
|
593
|
-
it('should return 401 when refresh token is expired', async () => {
|
|
594
|
-
vi.spyOn(tokenFns, 'validateAndDecodeAccessToken').mockImplementation(() => {
|
|
595
|
-
throw new Error('Token expired');
|
|
596
|
-
});
|
|
597
|
-
vi.spyOn(tokenFns, 'generateTokensFromRefreshToken').mockRejectedValue(new Error('Expired_Refresh_Token'));
|
|
598
|
-
const authPluginWithAccessCookie = configurePlugin({
|
|
599
|
-
keys: {
|
|
600
|
-
secretKey: '',
|
|
601
|
-
publicKey: '',
|
|
602
|
-
},
|
|
603
|
-
dataProvider,
|
|
604
|
-
cookies: {
|
|
605
|
-
enabled: true,
|
|
606
|
-
accessTokenInCookie: true,
|
|
607
|
-
},
|
|
608
|
-
});
|
|
609
|
-
const app = await createApp({
|
|
610
|
-
plugins: [authPluginWithAccessCookie],
|
|
611
|
-
});
|
|
612
|
-
const res = await request(app)
|
|
613
|
-
.post('/auth/restore')
|
|
614
|
-
.set('Cookie', [
|
|
615
|
-
'dotpress_access_token=expired-access-token',
|
|
616
|
-
'dotpress_refresh_token=expired-refresh-token',
|
|
617
|
-
]);
|
|
618
|
-
expect(res.status).toBe(401);
|
|
619
|
-
});
|
|
620
|
-
});
|
|
621
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|