@lobehub/chat 1.122.4 → 1.122.5
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/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/utils/src/server/__tests__/auth.test.ts +1 -1
- package/packages/utils/src/server/auth.ts +2 -2
- package/src/app/(backend)/api/auth/adapter/route.ts +137 -0
- package/src/app/(backend)/api/webhooks/logto/route.ts +9 -0
- package/src/config/auth.ts +4 -0
- package/src/libs/next-auth/adapter/index.ts +103 -201
- package/src/libs/next-auth/auth.config.ts +22 -10
- package/src/libs/next-auth/index.ts +11 -24
- package/src/libs/trpc/edge/context.ts +2 -2
- package/src/libs/trpc/lambda/context.ts +2 -2
- package/src/middleware.ts +2 -2
- package/src/server/routers/lambda/user.test.ts +4 -17
- package/src/server/routers/lambda/user.ts +6 -15
- package/src/server/services/nextAuthUser/index.ts +282 -6
- package/packages/database/src/server/models/__tests__/nextauth.test.ts +0 -556
- package/src/libs/next-auth/edge.ts +0 -26
- package/src/server/services/nextAuthUser/index.test.ts +0 -108
- /package/src/{libs/next-auth/adapter → server/services/nextAuthUser}/utils.ts +0 -0
@@ -1,556 +0,0 @@
|
|
1
|
-
import type {
|
2
|
-
AdapterAccount,
|
3
|
-
AdapterAuthenticator,
|
4
|
-
AdapterSession,
|
5
|
-
AdapterUser,
|
6
|
-
VerificationToken,
|
7
|
-
} from '@auth/core/adapters';
|
8
|
-
import { eq } from 'drizzle-orm';
|
9
|
-
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
10
|
-
|
11
|
-
import { LobeNextAuthDbAdapter } from '@/libs/next-auth/adapter';
|
12
|
-
|
13
|
-
import { getTestDBInstance } from '../../../core/dbForTest';
|
14
|
-
import {
|
15
|
-
nextauthAccounts,
|
16
|
-
nextauthAuthenticators,
|
17
|
-
nextauthSessions,
|
18
|
-
nextauthVerificationTokens,
|
19
|
-
users,
|
20
|
-
} from '../../../schemas';
|
21
|
-
|
22
|
-
let serverDB = await getTestDBInstance();
|
23
|
-
|
24
|
-
let nextAuthAdapter = LobeNextAuthDbAdapter(serverDB);
|
25
|
-
|
26
|
-
const userId = 'user-db';
|
27
|
-
const user: AdapterUser = {
|
28
|
-
id: userId,
|
29
|
-
name: 'John Doe',
|
30
|
-
email: 'john.doe@example.com',
|
31
|
-
emailVerified: new Date(),
|
32
|
-
image: 'https://example.com/avatar.jpg',
|
33
|
-
};
|
34
|
-
|
35
|
-
const sessionToken = 'session-token';
|
36
|
-
|
37
|
-
beforeAll(async () => {
|
38
|
-
await serverDB.delete(users);
|
39
|
-
await serverDB.delete(nextauthAccounts);
|
40
|
-
await serverDB.delete(nextauthAuthenticators);
|
41
|
-
await serverDB.delete(nextauthSessions);
|
42
|
-
await serverDB.delete(nextauthVerificationTokens);
|
43
|
-
});
|
44
|
-
|
45
|
-
beforeEach(async () => {
|
46
|
-
process.env.KEY_VAULTS_SECRET = 'ofQiJCXLF8mYemwfMWLOHoHimlPu91YmLfU7YZ4lreQ=';
|
47
|
-
// insert a user
|
48
|
-
// @ts-expect-error: createUser is defined
|
49
|
-
await nextAuthAdapter.createUser(user);
|
50
|
-
});
|
51
|
-
|
52
|
-
afterEach(async () => {
|
53
|
-
await serverDB.delete(users);
|
54
|
-
});
|
55
|
-
|
56
|
-
afterAll(async () => {
|
57
|
-
await serverDB.delete(users);
|
58
|
-
await serverDB.delete(nextauthAccounts);
|
59
|
-
await serverDB.delete(nextauthAuthenticators);
|
60
|
-
await serverDB.delete(nextauthSessions);
|
61
|
-
await serverDB.delete(nextauthVerificationTokens);
|
62
|
-
process.env.KEY_VAULTS_SECRET = undefined;
|
63
|
-
});
|
64
|
-
|
65
|
-
/**
|
66
|
-
* The tests below only test the database operation functionality,
|
67
|
-
* the mock values might not represent the actual value
|
68
|
-
*/
|
69
|
-
describe('LobeNextAuthDbAdapter', () => {
|
70
|
-
describe('users', () => {
|
71
|
-
describe('createUser', () => {
|
72
|
-
it('should create a new user', async () => {
|
73
|
-
expect(nextAuthAdapter).toBeDefined();
|
74
|
-
expect(nextAuthAdapter.createUser).toBeDefined();
|
75
|
-
const anotherUserId = 'user-db-2';
|
76
|
-
const anotherUserName = 'John Doe 2';
|
77
|
-
const anotherEmail = 'john.doe.2@example.com';
|
78
|
-
// @ts-expect-error: createUser is defined
|
79
|
-
const createdUser = await nextAuthAdapter.createUser({
|
80
|
-
...user,
|
81
|
-
id: anotherUserId,
|
82
|
-
name: anotherUserName,
|
83
|
-
email: anotherEmail,
|
84
|
-
});
|
85
|
-
expect(createdUser).toBeDefined();
|
86
|
-
expect(createdUser.id).toBe(anotherUserId);
|
87
|
-
expect(createdUser.name).toBe(anotherUserName);
|
88
|
-
expect(createdUser.email).toBe(anotherEmail);
|
89
|
-
expect(createdUser.emailVerified).toBe(user.emailVerified);
|
90
|
-
expect(createdUser.image).toBe(user.image);
|
91
|
-
});
|
92
|
-
|
93
|
-
it('should not create a user if it already exists', async () => {
|
94
|
-
expect(nextAuthAdapter).toBeDefined();
|
95
|
-
expect(nextAuthAdapter.createUser).toBeDefined();
|
96
|
-
// @ts-expect-error: createUser is defined
|
97
|
-
await nextAuthAdapter.createUser(user);
|
98
|
-
// Should not create a new user if it already exists
|
99
|
-
expect(
|
100
|
-
await serverDB.query.users.findMany({ where: eq(users.email, user.email) }),
|
101
|
-
).toHaveLength(1);
|
102
|
-
});
|
103
|
-
|
104
|
-
it('should not create a user if email exist', async () => {
|
105
|
-
expect(nextAuthAdapter).toBeDefined();
|
106
|
-
expect(nextAuthAdapter.createUser).toBeDefined();
|
107
|
-
const anotherUserId = 'user-db-2';
|
108
|
-
const anotherUserName = 'John Doe 2';
|
109
|
-
// @ts-expect-error: createUser is defined
|
110
|
-
await nextAuthAdapter.createUser({
|
111
|
-
...user,
|
112
|
-
id: anotherUserId,
|
113
|
-
name: anotherUserName,
|
114
|
-
});
|
115
|
-
// Should not create a new user if email already exists
|
116
|
-
expect(
|
117
|
-
await serverDB.query.users.findMany({ where: eq(users.email, user.email) }),
|
118
|
-
).toHaveLength(1);
|
119
|
-
});
|
120
|
-
|
121
|
-
it('should create a user if id not exist and email is null', async () => {
|
122
|
-
// In previous version, it will link the account to the existing user if the email is null
|
123
|
-
// issue: https://github.com/lobehub/lobe-chat/issues/4918
|
124
|
-
expect(nextAuthAdapter).toBeDefined();
|
125
|
-
expect(nextAuthAdapter.createUser).toBeDefined();
|
126
|
-
|
127
|
-
const existUserId = 'user-db-1';
|
128
|
-
const existUserName = 'John Doe 1';
|
129
|
-
// @ts-expect-error: createUser is defined
|
130
|
-
await nextAuthAdapter.createUser({
|
131
|
-
...user,
|
132
|
-
id: existUserId,
|
133
|
-
name: existUserName,
|
134
|
-
email: '',
|
135
|
-
});
|
136
|
-
|
137
|
-
const anotherUserId = 'user-db-2';
|
138
|
-
const anotherUserName = 'John Doe 2';
|
139
|
-
// @ts-expect-error: createUser is defined
|
140
|
-
await nextAuthAdapter.createUser({
|
141
|
-
...user,
|
142
|
-
id: anotherUserId,
|
143
|
-
name: anotherUserName,
|
144
|
-
email: '',
|
145
|
-
});
|
146
|
-
// Should create a new user if id not exists and email is null
|
147
|
-
expect(
|
148
|
-
await serverDB.query.users.findMany({ where: eq(users.id, anotherUserId) }),
|
149
|
-
).toHaveLength(1);
|
150
|
-
});
|
151
|
-
|
152
|
-
it('should create a user if id not exist even email is invalid type', async () => {
|
153
|
-
// In previous version, it will link the account to the existing user if the email is null
|
154
|
-
// issue: https://github.com/lobehub/lobe-chat/issues/4918
|
155
|
-
expect(nextAuthAdapter).toBeDefined();
|
156
|
-
expect(nextAuthAdapter.createUser).toBeDefined();
|
157
|
-
|
158
|
-
const existUserId = 'user-db-1';
|
159
|
-
const existUserName = 'John Doe 1';
|
160
|
-
// @ts-expect-error: createUser is defined
|
161
|
-
await nextAuthAdapter.createUser({
|
162
|
-
...user,
|
163
|
-
id: existUserId,
|
164
|
-
name: existUserName,
|
165
|
-
email: Object({}), // assign a non-string value
|
166
|
-
});
|
167
|
-
|
168
|
-
const anotherUserId = 'user-db-2';
|
169
|
-
const anotherUserName = 'John Doe 2';
|
170
|
-
// @ts-expect-error: createUser is defined
|
171
|
-
await nextAuthAdapter.createUser({
|
172
|
-
...user,
|
173
|
-
id: anotherUserId,
|
174
|
-
name: anotherUserName,
|
175
|
-
// @ts-expect-error: try to assign undefined value
|
176
|
-
email: undefined,
|
177
|
-
});
|
178
|
-
|
179
|
-
// Should create a new user if id not exists and email is null
|
180
|
-
expect(
|
181
|
-
await serverDB.query.users.findMany({ where: eq(users.id, anotherUserId) }),
|
182
|
-
).toHaveLength(1);
|
183
|
-
});
|
184
|
-
});
|
185
|
-
|
186
|
-
describe('deleteUser', () => {
|
187
|
-
it('should delete a user', async () => {
|
188
|
-
expect(nextAuthAdapter).toBeDefined();
|
189
|
-
expect(nextAuthAdapter.deleteUser).toBeDefined();
|
190
|
-
// @ts-expect-error: deleteUser is defined
|
191
|
-
await nextAuthAdapter.deleteUser(userId);
|
192
|
-
const deletedUser = await serverDB.query.users.findFirst({ where: eq(users.id, userId) });
|
193
|
-
expect(deletedUser).toBeUndefined();
|
194
|
-
});
|
195
|
-
});
|
196
|
-
|
197
|
-
describe('getUser', () => {
|
198
|
-
it('should get a user', async () => {
|
199
|
-
expect(nextAuthAdapter).toBeDefined();
|
200
|
-
expect(nextAuthAdapter.getUser).toBeDefined();
|
201
|
-
// @ts-expect-error: getUser is defined
|
202
|
-
const fetchedUser = await nextAuthAdapter.getUser(userId);
|
203
|
-
expect(fetchedUser).toBeDefined();
|
204
|
-
expect(fetchedUser?.id).toBe(user.id);
|
205
|
-
expect(fetchedUser?.name).toBe(user.name);
|
206
|
-
expect(fetchedUser?.email).toBe(user.email);
|
207
|
-
expect(fetchedUser?.emailVerified).toEqual(user.emailVerified);
|
208
|
-
expect(fetchedUser?.image).toBe(user.image);
|
209
|
-
});
|
210
|
-
});
|
211
|
-
|
212
|
-
describe('getUserByEmail', () => {
|
213
|
-
it('should get a user by email', async () => {
|
214
|
-
expect(nextAuthAdapter).toBeDefined();
|
215
|
-
expect(nextAuthAdapter.getUserByEmail).toBeDefined();
|
216
|
-
// @ts-expect-error: getUserByEmail is defined
|
217
|
-
const fetchedUser = await nextAuthAdapter.getUserByEmail(user.email);
|
218
|
-
expect(fetchedUser).toBeDefined();
|
219
|
-
expect(fetchedUser?.id).toBe(user.id);
|
220
|
-
expect(fetchedUser?.name).toBe(user.name);
|
221
|
-
expect(fetchedUser?.email).toBe(user.email);
|
222
|
-
expect(fetchedUser?.emailVerified).toEqual(user.emailVerified);
|
223
|
-
expect(fetchedUser?.image).toBe(user.image);
|
224
|
-
});
|
225
|
-
});
|
226
|
-
|
227
|
-
describe('getUserByAccount', () => {
|
228
|
-
it('should get a user by account', async () => {
|
229
|
-
const account: AdapterAccount = {
|
230
|
-
providerAccountId: 'provider-account-id',
|
231
|
-
userId: userId,
|
232
|
-
provider: 'auth0',
|
233
|
-
type: 'email',
|
234
|
-
};
|
235
|
-
// @ts-expect-error: linkAccount is defined
|
236
|
-
await nextAuthAdapter.linkAccount(account);
|
237
|
-
expect(nextAuthAdapter).toBeDefined();
|
238
|
-
expect(nextAuthAdapter.getUserByAccount).toBeDefined();
|
239
|
-
// @ts-expect-error: getUserByAccount is defined
|
240
|
-
const fetchedUser = await nextAuthAdapter.getUserByAccount(account);
|
241
|
-
expect(fetchedUser).toBeDefined();
|
242
|
-
expect(fetchedUser?.id).toBe(user.id);
|
243
|
-
expect(fetchedUser?.name).toBe(user.name);
|
244
|
-
expect(fetchedUser?.email).toBe(user.email);
|
245
|
-
expect(fetchedUser?.emailVerified).toEqual(user.emailVerified);
|
246
|
-
expect(fetchedUser?.image).toBe(user.image);
|
247
|
-
});
|
248
|
-
});
|
249
|
-
|
250
|
-
describe('updateUser', () => {
|
251
|
-
it('should update a user', async () => {
|
252
|
-
expect(nextAuthAdapter).toBeDefined();
|
253
|
-
expect(nextAuthAdapter.updateUser).toBeDefined();
|
254
|
-
const updatedName = 'updated' + user.name;
|
255
|
-
const updatedEmail = 'updated' + user.email;
|
256
|
-
// @ts-expect-error: updateUser is defined
|
257
|
-
const updatedUser = await nextAuthAdapter.updateUser({
|
258
|
-
id: userId,
|
259
|
-
name: updatedName,
|
260
|
-
email: updatedEmail,
|
261
|
-
});
|
262
|
-
expect(updatedUser).toBeDefined();
|
263
|
-
expect(updatedUser.id).toBe(userId);
|
264
|
-
expect(updatedUser.name).toBe(updatedName);
|
265
|
-
expect(updatedUser.email).toBe(updatedEmail);
|
266
|
-
});
|
267
|
-
});
|
268
|
-
});
|
269
|
-
|
270
|
-
describe('authenticators', () => {
|
271
|
-
describe('createAuthenticator', () => {
|
272
|
-
it('should create a new authenticator', async () => {
|
273
|
-
// Create an authenticator and link to the exists user
|
274
|
-
const params: AdapterAuthenticator = {
|
275
|
-
credentialBackedUp: false,
|
276
|
-
credentialDeviceType: 'type',
|
277
|
-
credentialID: 'some-id',
|
278
|
-
credentialPublicKey: 'some-key',
|
279
|
-
userId: userId,
|
280
|
-
providerAccountId: 'provider-account-id',
|
281
|
-
counter: 1,
|
282
|
-
};
|
283
|
-
expect(nextAuthAdapter).toBeDefined();
|
284
|
-
expect(nextAuthAdapter.createAuthenticator).toBeDefined();
|
285
|
-
// @ts-expect-error: createAuthenticator is defined
|
286
|
-
const authenticator = await nextAuthAdapter.createAuthenticator(params);
|
287
|
-
|
288
|
-
expect(authenticator).toBeDefined();
|
289
|
-
expect(authenticator.userId).toBe(params.userId);
|
290
|
-
expect(authenticator.providerAccountId).toBe(params.providerAccountId);
|
291
|
-
});
|
292
|
-
});
|
293
|
-
|
294
|
-
describe('getAuthenticator', () => {
|
295
|
-
it('should get an authenticator', async () => {
|
296
|
-
// Create an authenticator and link to the exists user
|
297
|
-
const params: AdapterAuthenticator = {
|
298
|
-
credentialBackedUp: false,
|
299
|
-
credentialDeviceType: 'type',
|
300
|
-
credentialID: 'some-id',
|
301
|
-
credentialPublicKey: 'some-key',
|
302
|
-
userId: userId,
|
303
|
-
providerAccountId: 'provider-account-id',
|
304
|
-
counter: 1,
|
305
|
-
};
|
306
|
-
// @ts-expect-error: createAuthenticator is defined
|
307
|
-
await nextAuthAdapter.createAuthenticator(params);
|
308
|
-
expect(nextAuthAdapter).toBeDefined();
|
309
|
-
expect(nextAuthAdapter.getAuthenticator).toBeDefined();
|
310
|
-
// @ts-expect-error: getAuthenticator is defined
|
311
|
-
const fetchedAuthenticator = await nextAuthAdapter.getAuthenticator(params.credentialID);
|
312
|
-
expect(fetchedAuthenticator).toBeDefined();
|
313
|
-
expect(fetchedAuthenticator?.userId).toBe(params.userId);
|
314
|
-
expect(fetchedAuthenticator?.providerAccountId).toBe(params.providerAccountId);
|
315
|
-
});
|
316
|
-
});
|
317
|
-
|
318
|
-
describe('updateAuthenticatorCounter', () => {
|
319
|
-
it('should update an authenticator counter', async () => {
|
320
|
-
// Create an authenticator and link to the exists user
|
321
|
-
const params: AdapterAuthenticator = {
|
322
|
-
credentialBackedUp: false,
|
323
|
-
credentialDeviceType: 'type',
|
324
|
-
credentialID: 'some-id',
|
325
|
-
credentialPublicKey: 'some-key',
|
326
|
-
userId: userId,
|
327
|
-
providerAccountId: 'provider-account-id',
|
328
|
-
counter: 1,
|
329
|
-
};
|
330
|
-
// @ts-expect-error: createAuthenticator is defined
|
331
|
-
await nextAuthAdapter.createAuthenticator(params);
|
332
|
-
expect(nextAuthAdapter).toBeDefined();
|
333
|
-
expect(nextAuthAdapter.updateAuthenticatorCounter).toBeDefined();
|
334
|
-
// @ts-expect-error: updateAuthenticatorCounter is defined
|
335
|
-
const updatedAuthenticator = await nextAuthAdapter.updateAuthenticatorCounter(
|
336
|
-
params.credentialID,
|
337
|
-
2,
|
338
|
-
);
|
339
|
-
expect(updatedAuthenticator).toBeDefined();
|
340
|
-
expect(updatedAuthenticator.counter).toBe(2);
|
341
|
-
});
|
342
|
-
});
|
343
|
-
|
344
|
-
describe('listAuthenticatorsByUserId', () => {
|
345
|
-
it('should list authenticators by user id', async () => {
|
346
|
-
// Create an authenticator and link to the exists user
|
347
|
-
const params: AdapterAuthenticator = {
|
348
|
-
credentialBackedUp: false,
|
349
|
-
credentialDeviceType: 'type',
|
350
|
-
credentialID: 'some-id',
|
351
|
-
credentialPublicKey: 'some-key',
|
352
|
-
userId: userId,
|
353
|
-
providerAccountId: 'provider-account-id',
|
354
|
-
counter: 1,
|
355
|
-
};
|
356
|
-
// @ts-expect-error: createAuthenticator is defined
|
357
|
-
await nextAuthAdapter.createAuthenticator(params);
|
358
|
-
expect(nextAuthAdapter).toBeDefined();
|
359
|
-
expect(nextAuthAdapter.listAuthenticatorsByUserId).toBeDefined();
|
360
|
-
// @ts-expect-error: listAuthenticatorsByUserId is defined
|
361
|
-
const authenticators = await nextAuthAdapter.listAuthenticatorsByUserId(userId);
|
362
|
-
expect(authenticators).toBeDefined();
|
363
|
-
expect(authenticators.length).toBeGreaterThan(0);
|
364
|
-
expect(authenticators[0].userId).toBe(params.userId);
|
365
|
-
expect(authenticators[0].providerAccountId).toBe(params.providerAccountId);
|
366
|
-
});
|
367
|
-
});
|
368
|
-
});
|
369
|
-
|
370
|
-
describe('session', () => {
|
371
|
-
describe('createSession', () => {
|
372
|
-
it('should create a new session', async () => {
|
373
|
-
const data: AdapterSession = {
|
374
|
-
sessionToken,
|
375
|
-
userId: userId,
|
376
|
-
expires: new Date(),
|
377
|
-
};
|
378
|
-
expect(nextAuthAdapter).toBeDefined();
|
379
|
-
expect(nextAuthAdapter.createSession).toBeDefined();
|
380
|
-
|
381
|
-
// @ts-expect-error: createSession is defined
|
382
|
-
const session = await nextAuthAdapter.createSession(data);
|
383
|
-
|
384
|
-
expect(session).toBeDefined();
|
385
|
-
expect(session.sessionToken).toBe(data.sessionToken);
|
386
|
-
expect(session.userId).toBe(data.userId);
|
387
|
-
expect(session.expires).toEqual(data.expires);
|
388
|
-
});
|
389
|
-
});
|
390
|
-
|
391
|
-
describe('updateSession', () => {
|
392
|
-
it('should update a session', async () => {
|
393
|
-
const data: AdapterSession = {
|
394
|
-
sessionToken,
|
395
|
-
userId: userId,
|
396
|
-
expires: new Date(),
|
397
|
-
};
|
398
|
-
// @ts-expect-error: createSession is defined
|
399
|
-
await nextAuthAdapter.createSession(data);
|
400
|
-
const updatedExpires = new Date();
|
401
|
-
expect(nextAuthAdapter).toBeDefined();
|
402
|
-
expect(nextAuthAdapter.updateSession).toBeDefined();
|
403
|
-
// @ts-expect-error: updateSession is defined
|
404
|
-
const updatedSession = await nextAuthAdapter.updateSession({
|
405
|
-
sessionToken,
|
406
|
-
expires: updatedExpires,
|
407
|
-
});
|
408
|
-
expect(updatedSession).toBeDefined();
|
409
|
-
expect(updatedSession?.sessionToken).toBe(data.sessionToken);
|
410
|
-
expect(updatedSession?.expires).toEqual(updatedExpires);
|
411
|
-
});
|
412
|
-
});
|
413
|
-
|
414
|
-
describe('getSessionAndUser', () => {
|
415
|
-
it('should get a session and user', async () => {
|
416
|
-
// create session
|
417
|
-
const data = {
|
418
|
-
sessionToken,
|
419
|
-
userId: userId,
|
420
|
-
expires: new Date(),
|
421
|
-
};
|
422
|
-
// @ts-expect-error: createSession is defined
|
423
|
-
await nextAuthAdapter.createSession(data);
|
424
|
-
|
425
|
-
// @ts-expect-error: getSessionAndUser is defined
|
426
|
-
const sessionAndUser = await nextAuthAdapter.getSessionAndUser(sessionToken);
|
427
|
-
expect(sessionAndUser?.session.sessionToken).toBe(sessionToken);
|
428
|
-
expect(sessionAndUser?.user.id).toBe(user.id);
|
429
|
-
});
|
430
|
-
});
|
431
|
-
|
432
|
-
describe('deleteSession', () => {
|
433
|
-
it('should delete a session', async () => {
|
434
|
-
const data = {
|
435
|
-
sessionToken,
|
436
|
-
userId: userId,
|
437
|
-
expires: new Date(),
|
438
|
-
};
|
439
|
-
// @ts-expect-error: createSession is defined
|
440
|
-
await nextAuthAdapter.createSession(data);
|
441
|
-
|
442
|
-
// @ts-expect-error: deleteSession is defined
|
443
|
-
await nextAuthAdapter.deleteSession(sessionToken);
|
444
|
-
const session = await serverDB.query.nextauthSessions.findFirst({
|
445
|
-
where: eq(nextauthSessions.sessionToken, sessionToken),
|
446
|
-
});
|
447
|
-
expect(session).toBeUndefined();
|
448
|
-
});
|
449
|
-
});
|
450
|
-
});
|
451
|
-
|
452
|
-
describe('verificationToken', () => {
|
453
|
-
describe('createVerificationToken', () => {
|
454
|
-
it('should create a new verification token', async () => {
|
455
|
-
const token: VerificationToken = {
|
456
|
-
identifier: 'identifier',
|
457
|
-
expires: new Date(),
|
458
|
-
token: 'token',
|
459
|
-
};
|
460
|
-
// @ts-expect-error: createVerificationToken is defined
|
461
|
-
const createdToken = await nextAuthAdapter.createVerificationToken(token);
|
462
|
-
expect(createdToken).toBeDefined();
|
463
|
-
expect(createdToken?.identifier).toBe(token.identifier);
|
464
|
-
expect(createdToken?.expires).toEqual(token.expires);
|
465
|
-
expect(createdToken?.token).toBe(token.token);
|
466
|
-
});
|
467
|
-
});
|
468
|
-
|
469
|
-
describe('useVerificationToken', () => {
|
470
|
-
it('should use a verification token if exist', async () => {
|
471
|
-
const token: VerificationToken = {
|
472
|
-
identifier: 'identifier',
|
473
|
-
expires: new Date(),
|
474
|
-
token: 'token2',
|
475
|
-
};
|
476
|
-
// @ts-expect-error: createVerificationToken is defined
|
477
|
-
await nextAuthAdapter.createVerificationToken(token);
|
478
|
-
// @ts-expect-error: useVerificationToken is defined
|
479
|
-
await nextAuthAdapter.useVerificationToken(token.token);
|
480
|
-
});
|
481
|
-
|
482
|
-
it('should return null if the token does not exist', async () => {
|
483
|
-
const token: VerificationToken = {
|
484
|
-
identifier: 'identifier',
|
485
|
-
expires: new Date(),
|
486
|
-
token: 'token-not-exist',
|
487
|
-
};
|
488
|
-
// @ts-expect-error: useVerificationToken is defined
|
489
|
-
const result = await nextAuthAdapter.useVerificationToken(token.token);
|
490
|
-
expect(result).toBeNull();
|
491
|
-
});
|
492
|
-
});
|
493
|
-
});
|
494
|
-
|
495
|
-
describe('accounts', () => {
|
496
|
-
describe('linkAccount', () => {
|
497
|
-
it('should link an account', async () => {
|
498
|
-
const account: AdapterAccount = {
|
499
|
-
providerAccountId: 'provider-account-id',
|
500
|
-
userId: userId,
|
501
|
-
provider: 'auth0',
|
502
|
-
type: 'email',
|
503
|
-
};
|
504
|
-
// @ts-expect-error: linkAccount is defined
|
505
|
-
const insertedAccount = await nextAuthAdapter.linkAccount(account);
|
506
|
-
expect(insertedAccount).toBeDefined();
|
507
|
-
expect(insertedAccount?.providerAccountId).toBe(account.providerAccountId);
|
508
|
-
expect(insertedAccount?.userId).toBe(userId);
|
509
|
-
expect(insertedAccount?.provider).toBe(account.provider);
|
510
|
-
expect(insertedAccount?.type).toBe(account.type);
|
511
|
-
});
|
512
|
-
});
|
513
|
-
|
514
|
-
describe('getAccount', () => {
|
515
|
-
it('should get an account', async () => {
|
516
|
-
const account: AdapterAccount = {
|
517
|
-
providerAccountId: 'provider-account-id',
|
518
|
-
userId: userId,
|
519
|
-
provider: 'auth0',
|
520
|
-
type: 'email',
|
521
|
-
};
|
522
|
-
// @ts-expect-error: linkAccount is defined
|
523
|
-
await nextAuthAdapter.linkAccount(account);
|
524
|
-
// @ts-expect-error: getAccount is defined
|
525
|
-
const fetchedAccount = await nextAuthAdapter.getAccount(
|
526
|
-
account.providerAccountId,
|
527
|
-
account.provider,
|
528
|
-
);
|
529
|
-
expect(fetchedAccount).toBeDefined();
|
530
|
-
expect(fetchedAccount?.providerAccountId).toBe(account.providerAccountId);
|
531
|
-
expect(fetchedAccount?.userId).toBe(userId);
|
532
|
-
expect(fetchedAccount?.provider).toBe(account.provider);
|
533
|
-
expect(fetchedAccount?.type).toBe(account.type);
|
534
|
-
});
|
535
|
-
});
|
536
|
-
|
537
|
-
describe('unlinkAccount', () => {
|
538
|
-
it('should unlink an account', async () => {
|
539
|
-
const account: AdapterAccount = {
|
540
|
-
providerAccountId: 'provider-account-id',
|
541
|
-
userId: userId,
|
542
|
-
provider: 'auth0',
|
543
|
-
type: 'email',
|
544
|
-
};
|
545
|
-
// @ts-expect-error: linkAccount is defined
|
546
|
-
await nextAuthAdapter.linkAccount(account);
|
547
|
-
// @ts-expect-error: unlinkAccount is defined
|
548
|
-
await nextAuthAdapter.unlinkAccount(account);
|
549
|
-
const fetchedAccount = await serverDB.query.nextauthAccounts.findFirst({
|
550
|
-
where: eq(nextauthAccounts.providerAccountId, account.providerAccountId),
|
551
|
-
});
|
552
|
-
expect(fetchedAccount).toBeUndefined();
|
553
|
-
});
|
554
|
-
});
|
555
|
-
});
|
556
|
-
});
|
@@ -1,26 +0,0 @@
|
|
1
|
-
import NextAuth from 'next-auth';
|
2
|
-
|
3
|
-
import authConfig from './auth.config';
|
4
|
-
|
5
|
-
/**
|
6
|
-
* NextAuth initialization without Database adapter
|
7
|
-
*
|
8
|
-
* @example
|
9
|
-
* ```ts
|
10
|
-
* import NextAuthEdge from '@/libs/next-auth/edge';
|
11
|
-
* const { auth } = NextAuthEdge;
|
12
|
-
* ```
|
13
|
-
*
|
14
|
-
* @note
|
15
|
-
* We currently use `jwt` strategy for session management.
|
16
|
-
* So you don't need to import `signIn` or `signOut` from
|
17
|
-
* this module, just import from `next-auth` directly.
|
18
|
-
*
|
19
|
-
* Inside react component
|
20
|
-
* @example
|
21
|
-
* ```ts
|
22
|
-
* import { signOut } from 'next-auth/react';
|
23
|
-
* signOut();
|
24
|
-
* ```
|
25
|
-
*/
|
26
|
-
export default NextAuth(authConfig);
|
@@ -1,108 +0,0 @@
|
|
1
|
-
// @vitest-environment node
|
2
|
-
import { NextResponse } from 'next/server';
|
3
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
4
|
-
|
5
|
-
import { UserModel } from '@/database/models/user';
|
6
|
-
import { UserItem } from '@/database/schemas';
|
7
|
-
import { serverDB } from '@/database/server';
|
8
|
-
import { pino } from '@/libs/logger';
|
9
|
-
|
10
|
-
import { NextAuthUserService } from './index';
|
11
|
-
|
12
|
-
vi.mock('@/libs/logger', () => ({
|
13
|
-
pino: {
|
14
|
-
info: vi.fn(),
|
15
|
-
warn: vi.fn(),
|
16
|
-
},
|
17
|
-
}));
|
18
|
-
|
19
|
-
vi.mock('@/database/models/user');
|
20
|
-
vi.mock('@/database/server');
|
21
|
-
|
22
|
-
describe('NextAuthUserService', () => {
|
23
|
-
let service: NextAuthUserService;
|
24
|
-
|
25
|
-
beforeEach(async () => {
|
26
|
-
vi.clearAllMocks();
|
27
|
-
service = new NextAuthUserService(serverDB);
|
28
|
-
});
|
29
|
-
|
30
|
-
describe('safeUpdateUser', () => {
|
31
|
-
const mockUser = {
|
32
|
-
id: 'user-123',
|
33
|
-
email: 'test@example.com',
|
34
|
-
};
|
35
|
-
|
36
|
-
const mockAccount = {
|
37
|
-
provider: 'github',
|
38
|
-
providerAccountId: '12345',
|
39
|
-
};
|
40
|
-
|
41
|
-
const mockUpdateData: Partial<UserItem> = {
|
42
|
-
avatar: 'https://example.com/avatar.jpg',
|
43
|
-
email: 'new@example.com',
|
44
|
-
fullName: 'Test User',
|
45
|
-
};
|
46
|
-
|
47
|
-
it('should update user when user is found', async () => {
|
48
|
-
const mockUserModel = {
|
49
|
-
updateUser: vi.fn().mockResolvedValue({}),
|
50
|
-
};
|
51
|
-
|
52
|
-
vi.mocked(UserModel).mockImplementation(() => mockUserModel as any);
|
53
|
-
|
54
|
-
// Mock the adapter directly on the service instance
|
55
|
-
service.adapter = {
|
56
|
-
getUserByAccount: vi.fn().mockResolvedValue(mockUser),
|
57
|
-
};
|
58
|
-
|
59
|
-
const response = await service.safeUpdateUser(mockAccount, mockUpdateData);
|
60
|
-
|
61
|
-
expect(pino.info).toHaveBeenCalledWith(
|
62
|
-
`updating user "${JSON.stringify(mockAccount)}" due to webhook`,
|
63
|
-
);
|
64
|
-
|
65
|
-
expect(service.adapter.getUserByAccount).toHaveBeenCalledWith(mockAccount);
|
66
|
-
expect(UserModel).toHaveBeenCalledWith(serverDB, mockUser.id);
|
67
|
-
expect(mockUserModel.updateUser).toHaveBeenCalledWith(mockUpdateData);
|
68
|
-
|
69
|
-
expect(response).toBeInstanceOf(NextResponse);
|
70
|
-
expect(response.status).toBe(200);
|
71
|
-
const data = await response.json();
|
72
|
-
expect(data).toEqual({ message: 'user updated', success: true });
|
73
|
-
});
|
74
|
-
|
75
|
-
it('should handle case when user is not found', async () => {
|
76
|
-
// Mock the adapter directly on the service instance
|
77
|
-
service.adapter = {
|
78
|
-
getUserByAccount: vi.fn().mockResolvedValue(null),
|
79
|
-
};
|
80
|
-
|
81
|
-
const response = await service.safeUpdateUser(mockAccount, mockUpdateData);
|
82
|
-
|
83
|
-
expect(pino.warn).toHaveBeenCalledWith(
|
84
|
-
`[${mockAccount.provider}]: Webhooks handler user "${JSON.stringify(mockAccount)}" update for "${JSON.stringify(mockUpdateData)}", but no user was found by the providerAccountId.`,
|
85
|
-
);
|
86
|
-
|
87
|
-
expect(UserModel).not.toHaveBeenCalled();
|
88
|
-
|
89
|
-
expect(response).toBeInstanceOf(NextResponse);
|
90
|
-
expect(response.status).toBe(200);
|
91
|
-
const data = await response.json();
|
92
|
-
expect(data).toEqual({ message: 'user updated', success: true });
|
93
|
-
});
|
94
|
-
|
95
|
-
it('should handle errors during user update', async () => {
|
96
|
-
const mockError = new Error('Database error');
|
97
|
-
|
98
|
-
// Mock the adapter directly on the service instance
|
99
|
-
service.adapter = {
|
100
|
-
getUserByAccount: vi.fn().mockRejectedValue(mockError),
|
101
|
-
};
|
102
|
-
|
103
|
-
await expect(service.safeUpdateUser(mockAccount, mockUpdateData)).rejects.toThrow(mockError);
|
104
|
-
|
105
|
-
expect(UserModel).not.toHaveBeenCalled();
|
106
|
-
});
|
107
|
-
});
|
108
|
-
});
|
File without changes
|