@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.
@@ -1,23 +1,32 @@
1
1
  import type { NextAuthConfig } from 'next-auth';
2
2
 
3
- import { authEnv } from '@/config/auth';
4
-
5
3
  import { ssoProviders } from './sso-providers';
4
+ import { LobeNextAuthDbAdapter } from './adapter';
5
+ import { getAuthConfig } from '@/config/auth';
6
+
7
+ const {
8
+ NEXT_AUTH_DEBUG,
9
+ NEXT_AUTH_SECRET,
10
+ NEXT_AUTH_SSO_SESSION_STRATEGY,
11
+ NEXT_AUTH_SSO_PROVIDERS,
12
+ NEXT_PUBLIC_ENABLE_NEXT_AUTH
13
+ } = getAuthConfig();
6
14
 
7
15
  export const initSSOProviders = () => {
8
- return authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH
9
- ? authEnv.NEXT_AUTH_SSO_PROVIDERS.split(/[,,]/).map((provider) => {
10
- const validProvider = ssoProviders.find((item) => item.id === provider.trim());
16
+ return NEXT_PUBLIC_ENABLE_NEXT_AUTH
17
+ ? NEXT_AUTH_SSO_PROVIDERS.split(/[,,]/).map((provider) => {
18
+ const validProvider = ssoProviders.find((item) => item.id === provider.trim());
11
19
 
12
- if (validProvider) return validProvider.provider;
20
+ if (validProvider) return validProvider.provider;
13
21
 
14
- throw new Error(`[NextAuth] provider ${provider} is not supported`);
15
- })
22
+ throw new Error(`[NextAuth] provider ${provider} is not supported`);
23
+ })
16
24
  : [];
17
25
  };
18
26
 
19
27
  // Notice this is only an object, not a full Auth.js instance
20
28
  export default {
29
+ adapter: LobeNextAuthDbAdapter(),
21
30
  callbacks: {
22
31
  // Note: Data processing order of callback: authorize --> jwt --> session
23
32
  async jwt({ token, user }) {
@@ -39,12 +48,15 @@ export default {
39
48
  return session;
40
49
  },
41
50
  },
42
- debug: authEnv.NEXT_AUTH_DEBUG,
51
+ debug: NEXT_AUTH_DEBUG,
43
52
  pages: {
44
53
  error: '/next-auth/error',
45
54
  signIn: '/next-auth/signin',
46
55
  },
47
56
  providers: initSSOProviders(),
48
- secret: authEnv.NEXT_AUTH_SECRET,
57
+ secret: NEXT_AUTH_SECRET,
58
+ session: {
59
+ strategy: NEXT_AUTH_SSO_SESSION_STRATEGY,
60
+ },
49
61
  trustHost: process.env?.AUTH_TRUST_HOST ? process.env.AUTH_TRUST_HOST === 'true' : true,
50
62
  } satisfies NextAuthConfig;
@@ -1,33 +1,20 @@
1
1
  import NextAuth from 'next-auth';
2
2
 
3
- import { getServerDBConfig } from '@/config/db';
4
- import { serverDB } from '@/database/server';
5
-
6
- import { LobeNextAuthDbAdapter } from './adapter';
7
- import config from './auth.config';
8
-
9
- const { NEXT_PUBLIC_ENABLED_SERVER_SERVICE } = getServerDBConfig();
3
+ import authConfig from './auth.config';
10
4
 
11
5
  /**
12
- * NextAuth initialization with Database adapter
6
+ * NextAuth initialization without Database adapter
7
+ *
8
+ * @note
9
+ * We currently use `jwt` strategy for session management.
10
+ * So you don't need to import `signIn` or `signOut` from
11
+ * this module, just import from `next-auth` directly.
13
12
  *
13
+ * Inside react component
14
14
  * @example
15
15
  * ```ts
16
- * import NextAuthNode from '@/libs/next-auth';
17
- * const { handlers } = NextAuthNode;
16
+ * import { signOut } from 'next-auth/react';
17
+ * signOut();
18
18
  * ```
19
- *
20
- * @note
21
- * If you meet the edge runtime compatible problem,
22
- * you can import from `@/libs/next-auth/edge` which is not initial with the database adapter.
23
- *
24
- * The difference and usage of the two different NextAuth modules is can be
25
- * ref to: https://github.com/lobehub/lobe-chat/pull/2935
26
19
  */
27
- export default NextAuth({
28
- ...config,
29
- adapter: NEXT_PUBLIC_ENABLED_SERVER_SERVICE ? LobeNextAuthDbAdapter(serverDB) : undefined,
30
- session: {
31
- strategy: 'jwt',
32
- },
33
- });
20
+ export default NextAuth(authConfig);
@@ -54,9 +54,9 @@ export const createEdgeContext = async (request: NextRequest): Promise<EdgeConte
54
54
 
55
55
  if (enableNextAuth) {
56
56
  try {
57
- const { default: NextAuthEdge } = await import('@/libs/next-auth/edge');
57
+ const { default: NextAuth } = await import('@/libs/next-auth');
58
58
 
59
- const session = await NextAuthEdge.auth();
59
+ const session = await NextAuth.auth();
60
60
  if (session && session?.user?.id) {
61
61
  auth = session.user;
62
62
  userId = session.user.id;
@@ -161,9 +161,9 @@ export const createLambdaContext = async (request: NextRequest): Promise<LambdaC
161
161
  if (enableNextAuth) {
162
162
  log('Attempting NextAuth authentication');
163
163
  try {
164
- const { default: NextAuthEdge } = await import('@/libs/next-auth/edge');
164
+ const { default: NextAuth } = await import('@/libs/next-auth');
165
165
 
166
- const session = await NextAuthEdge.auth();
166
+ const session = await NextAuth.auth();
167
167
  if (session && session?.user?.id) {
168
168
  auth = session.user;
169
169
  userId = session.user.id;
package/src/middleware.ts CHANGED
@@ -10,7 +10,7 @@ import { OAUTH_AUTHORIZED } from '@/const/auth';
10
10
  import { LOBE_LOCALE_COOKIE } from '@/const/locale';
11
11
  import { LOBE_THEME_APPEARANCE } from '@/const/theme';
12
12
  import { appEnv } from '@/envs/app';
13
- import NextAuthEdge from '@/libs/next-auth/edge';
13
+ import NextAuth from '@/libs/next-auth';
14
14
  import { Locales } from '@/locales/resources';
15
15
 
16
16
  import { oidcEnv } from './envs/oidc';
@@ -170,7 +170,7 @@ const isProtectedRoute = createRouteMatcher([
170
170
  ]);
171
171
 
172
172
  // Initialize an Edge compatible NextAuth middleware
173
- const nextAuthMiddleware = NextAuthEdge.auth(async (req) => {
173
+ const nextAuthMiddleware = NextAuth.auth((req) => {
174
174
  logNextAuth('NextAuth middleware processing request: %s %s', req.method, req.url);
175
175
 
176
176
  const response = defaultMiddleware(req);
@@ -6,8 +6,8 @@ import { MessageModel } from '@/database/models/message';
6
6
  import { SessionModel } from '@/database/models/session';
7
7
  import { UserModel, UserNotFoundError } from '@/database/models/user';
8
8
  import { serverDB } from '@/database/server';
9
- import { LobeNextAuthDbAdapter } from '@/libs/next-auth/adapter';
10
9
  import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
10
+ import { NextAuthUserService } from '@/server/services/nextAuthUser';
11
11
  import { UserService } from '@/server/services/user';
12
12
 
13
13
  import { userRouter } from './user';
@@ -24,10 +24,10 @@ vi.mock('@/database/server', () => ({
24
24
  vi.mock('@/database/models/message');
25
25
  vi.mock('@/database/models/session');
26
26
  vi.mock('@/database/models/user');
27
- vi.mock('@/libs/next-auth/adapter');
28
27
  vi.mock('@/server/modules/KeyVaultsEncrypt');
29
28
  vi.mock('@/server/modules/S3');
30
29
  vi.mock('@/server/services/user');
30
+ vi.mock('@/server/services/nextAuthUser');
31
31
  vi.mock('@/const/auth', () => ({
32
32
  enableClerk: true,
33
33
  }));
@@ -221,7 +221,7 @@ describe('userRouter', () => {
221
221
  type: 'oauth',
222
222
  };
223
223
 
224
- vi.mocked(LobeNextAuthDbAdapter).mockReturnValue({
224
+ vi.mocked(NextAuthUserService).mockReturnValue({
225
225
  getAccount: vi.fn().mockResolvedValue(mockAccount),
226
226
  unlinkAccount: vi.fn().mockResolvedValue(undefined),
227
227
  } as any);
@@ -237,7 +237,7 @@ describe('userRouter', () => {
237
237
  providerAccountId: '123',
238
238
  };
239
239
 
240
- vi.mocked(LobeNextAuthDbAdapter).mockReturnValue({
240
+ vi.mocked(NextAuthUserService).mockReturnValue({
241
241
  getAccount: vi.fn().mockResolvedValue(null),
242
242
  unlinkAccount: vi.fn(),
243
243
  } as any);
@@ -246,19 +246,6 @@ describe('userRouter', () => {
246
246
  userRouter.createCaller({ ...mockCtx }).unlinkSSOProvider(mockInput),
247
247
  ).rejects.toThrow('The account does not exist');
248
248
  });
249
-
250
- it('should throw error if adapter methods are not implemented', async () => {
251
- const mockInput = {
252
- provider: 'google',
253
- providerAccountId: '123',
254
- };
255
-
256
- vi.mocked(LobeNextAuthDbAdapter).mockReturnValue({} as any);
257
-
258
- await expect(
259
- userRouter.createCaller({ ...mockCtx }).unlinkSSOProvider(mockInput),
260
- ).rejects.toThrow('The method in LobeNextAuthDbAdapter `unlinkAccount` is not implemented');
261
- });
262
249
  });
263
250
 
264
251
  describe('updateSettings', () => {
@@ -9,12 +9,12 @@ import { SessionModel } from '@/database/models/session';
9
9
  import { UserModel, UserNotFoundError } from '@/database/models/user';
10
10
  import { ClerkAuth } from '@/libs/clerk-auth';
11
11
  import { pino } from '@/libs/logger';
12
- import { LobeNextAuthDbAdapter } from '@/libs/next-auth/adapter';
13
12
  import { authedProcedure, router } from '@/libs/trpc/lambda';
14
13
  import { serverDatabase } from '@/libs/trpc/lambda/middleware';
15
14
  import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
16
15
  import { S3 } from '@/server/modules/S3';
17
16
  import { FileService } from '@/server/services/file';
17
+ import { NextAuthUserService } from '@/server/services/nextAuthUser';
18
18
  import { UserService } from '@/server/services/user';
19
19
  import {
20
20
  NextAuthAccountSchame,
@@ -29,7 +29,7 @@ const userProcedure = authedProcedure.use(serverDatabase).use(async ({ ctx, next
29
29
  ctx: {
30
30
  clerkAuth: new ClerkAuth(),
31
31
  fileService: new FileService(ctx.serverDB, ctx.userId),
32
- nextAuthDbAdapter: LobeNextAuthDbAdapter(ctx.serverDB),
32
+ nextAuthUserService: new NextAuthUserService(ctx.serverDB),
33
33
  userModel: new UserModel(ctx.serverDB, ctx.userId),
34
34
  },
35
35
  });
@@ -134,19 +134,10 @@ export const userRouter = router({
134
134
 
135
135
  unlinkSSOProvider: userProcedure.input(NextAuthAccountSchame).mutation(async ({ ctx, input }) => {
136
136
  const { provider, providerAccountId } = input;
137
- if (
138
- ctx.nextAuthDbAdapter?.unlinkAccount &&
139
- typeof ctx.nextAuthDbAdapter.unlinkAccount === 'function' &&
140
- ctx.nextAuthDbAdapter?.getAccount &&
141
- typeof ctx.nextAuthDbAdapter.getAccount === 'function'
142
- ) {
143
- const account = await ctx.nextAuthDbAdapter.getAccount(providerAccountId, provider);
144
- // The userId can either get from ctx.nextAuth?.id or ctx.userId
145
- if (!account || account.userId !== ctx.userId) throw new Error('The account does not exist');
146
- await ctx.nextAuthDbAdapter.unlinkAccount({ provider, providerAccountId });
147
- } else {
148
- throw new Error('The method in LobeNextAuthDbAdapter `unlinkAccount` is not implemented');
149
- }
137
+ const account = await ctx.nextAuthUserService.getAccount(providerAccountId, provider);
138
+ // The userId can either get from ctx.nextAuth?.id or ctx.userId
139
+ if (!account || account.userId !== ctx.userId) throw new Error('The account does not exist');
140
+ await ctx.nextAuthUserService.unlinkAccount({ provider, providerAccountId });
150
141
  }),
151
142
 
152
143
  // 服务端上传头像
@@ -1,18 +1,33 @@
1
+ import { and, eq } from 'drizzle-orm';
2
+ import { Adapter, AdapterAccount } from 'next-auth/adapters';
1
3
  import { NextResponse } from 'next/server';
2
4
 
3
5
  import { UserModel } from '@/database/models/user';
4
- import { UserItem } from '@/database/schemas';
6
+ import {
7
+ UserItem,
8
+ nextauthAccounts,
9
+ nextauthAuthenticators,
10
+ nextauthSessions,
11
+ nextauthVerificationTokens,
12
+ users,
13
+ } from '@/database/schemas';
5
14
  import { LobeChatDatabase } from '@/database/type';
6
15
  import { pino } from '@/libs/logger';
7
- import { LobeNextAuthDbAdapter } from '@/libs/next-auth/adapter';
16
+ import { merge } from '@/utils/merge';
17
+
18
+ import { AgentService } from '../agent';
19
+ import {
20
+ mapAdapterUserToLobeUser,
21
+ mapAuthenticatorQueryResutlToAdapterAuthenticator,
22
+ mapLobeUserToAdapterUser,
23
+ partialMapAdapterUserToLobeUser,
24
+ } from './utils';
8
25
 
9
26
  export class NextAuthUserService {
10
- adapter;
11
27
  private db: LobeChatDatabase;
12
28
 
13
29
  constructor(db: LobeChatDatabase) {
14
30
  this.db = db;
15
- this.adapter = LobeNextAuthDbAdapter(db);
16
31
  }
17
32
 
18
33
  safeUpdateUser = async (
@@ -21,8 +36,7 @@ export class NextAuthUserService {
21
36
  ) => {
22
37
  pino.info(`updating user "${JSON.stringify({ provider, providerAccountId })}" due to webhook`);
23
38
  // 1. Find User by account
24
- // @ts-expect-error: Already impl in `LobeNextauthDbAdapter`
25
- const user = await this.adapter.getUserByAccount({
39
+ const user = await this.getUserByAccount({
26
40
  provider,
27
41
  providerAccountId,
28
42
  });
@@ -44,4 +58,266 @@ export class NextAuthUserService {
44
58
  }
45
59
  return NextResponse.json({ message: 'user updated', success: true }, { status: 200 });
46
60
  };
61
+
62
+ safeSignOutUser = async ({
63
+ providerAccountId,
64
+ provider,
65
+ }: {
66
+ provider: string;
67
+ providerAccountId: string;
68
+ }) => {
69
+ pino.info(`Signing out user "${JSON.stringify({ provider, providerAccountId })}"`);
70
+ const user = await this.getUserByAccount({
71
+ provider,
72
+ providerAccountId,
73
+ });
74
+
75
+ // 2. If found, Update user data from provider
76
+ if (user?.id) {
77
+ // Perform update
78
+ await this.db.delete(nextauthSessions).where(eq(nextauthSessions.userId, user.id));
79
+ } else {
80
+ pino.warn(
81
+ `[${provider}]: Webhooks handler user "${JSON.stringify({ provider, providerAccountId })}" to signout", but no user was found by the providerAccountId.`,
82
+ );
83
+ }
84
+ return NextResponse.json({ message: 'user signed out', success: true }, { status: 200 });
85
+ };
86
+
87
+ createAuthenticator: NonNullable<Adapter['createAuthenticator']> = async (authenticator) => {
88
+ return await this.db
89
+ .insert(nextauthAuthenticators)
90
+ .values(authenticator)
91
+ .returning()
92
+ .then((res: any) => res[0] ?? undefined);
93
+ };
94
+
95
+ createSession: NonNullable<Adapter['createSession']> = async (data) => {
96
+ return await this.db
97
+ .insert(nextauthSessions)
98
+ .values(data)
99
+ .returning()
100
+ .then((res: any) => res[0]);
101
+ };
102
+
103
+ createUser: NonNullable<Adapter['createUser']> = async (user) => {
104
+ const { id, name, email, emailVerified, image, providerAccountId } = user;
105
+ // return the user if it already exists
106
+ let existingUser =
107
+ email && typeof email === 'string' && email.trim()
108
+ ? await UserModel.findByEmail(this.db, email)
109
+ : undefined;
110
+ // If the user is not found by email, try to find by providerAccountId
111
+ if (!existingUser && providerAccountId) {
112
+ existingUser = await UserModel.findById(this.db, providerAccountId);
113
+ }
114
+ if (existingUser) {
115
+ const adapterUser = mapLobeUserToAdapterUser(existingUser);
116
+ return adapterUser;
117
+ }
118
+
119
+ // create a new user if it does not exist
120
+ // Use id from provider if it exists, otherwise use id assigned by next-auth
121
+ // ref: https://github.com/lobehub/lobe-chat/pull/2935
122
+ const uid = providerAccountId ?? id;
123
+ await UserModel.createUser(
124
+ this.db,
125
+ mapAdapterUserToLobeUser({
126
+ email,
127
+ emailVerified,
128
+ // Use providerAccountId as userid to identify if the user exists in a SSO provider
129
+ id: uid,
130
+ image,
131
+ name,
132
+ }),
133
+ );
134
+
135
+ // 3. Create an inbox session for the user
136
+ const agentService = new AgentService(this.db, uid);
137
+ await agentService.createInbox();
138
+
139
+ return { ...user, id: uid };
140
+ };
141
+
142
+ createVerificationToken: NonNullable<Adapter['createVerificationToken']> = async (data) => {
143
+ return await this.db
144
+ .insert(nextauthVerificationTokens)
145
+ .values(data)
146
+ .returning()
147
+ .then((res: any) => res[0]);
148
+ };
149
+
150
+ deleteSession: NonNullable<Adapter['deleteSession']> = async (sessionToken) => {
151
+ await this.db.delete(nextauthSessions).where(eq(nextauthSessions.sessionToken, sessionToken));
152
+ };
153
+
154
+ deleteUser: NonNullable<Adapter['deleteUser']> = async (id) => {
155
+ const user = await UserModel.findById(this.db, id);
156
+ if (!user) throw new Error('NextAuth: Delete User not found');
157
+ await UserModel.deleteUser(this.db, id);
158
+ };
159
+
160
+ getAccount: NonNullable<Adapter['getAccount']> = async (providerAccountId, provider) => {
161
+ return (await this.db
162
+ .select()
163
+ .from(nextauthAccounts)
164
+ .where(
165
+ and(
166
+ eq(nextauthAccounts.provider, provider),
167
+ eq(nextauthAccounts.providerAccountId, providerAccountId),
168
+ ),
169
+ )
170
+ .then((res: any) => res[0] ?? null)) as Promise<AdapterAccount | null>;
171
+ };
172
+
173
+ getAuthenticator: NonNullable<Adapter['getAuthenticator']> = async (credentialID) => {
174
+ const result = await this.db
175
+ .select()
176
+ .from(nextauthAuthenticators)
177
+ .where(eq(nextauthAuthenticators.credentialID, credentialID))
178
+ .then((res) => res[0] ?? null);
179
+ if (!result) throw new Error('NextAuthUserService: Failed to get authenticator');
180
+ return mapAuthenticatorQueryResutlToAdapterAuthenticator(result);
181
+ };
182
+
183
+ getSessionAndUser: NonNullable<Adapter['getSessionAndUser']> = async (sessionToken) => {
184
+ const result = await this.db
185
+ .select({
186
+ session: nextauthSessions,
187
+ user: users,
188
+ })
189
+ .from(nextauthSessions)
190
+ .where(eq(nextauthSessions.sessionToken, sessionToken))
191
+ .innerJoin(users, eq(users.id, nextauthSessions.userId))
192
+ .then((res: any) => (res.length > 0 ? res[0] : null));
193
+
194
+ if (!result) return null;
195
+ const adapterUser = mapLobeUserToAdapterUser(result.user);
196
+ if (!adapterUser) return null;
197
+ return {
198
+ session: result.session,
199
+ user: adapterUser,
200
+ };
201
+ };
202
+
203
+ getUser: NonNullable<Adapter['getUser']> = async (id) => {
204
+ const lobeUser = await UserModel.findById(this.db, id);
205
+ if (!lobeUser) return null;
206
+ return mapLobeUserToAdapterUser(lobeUser);
207
+ };
208
+
209
+ getUserByAccount: NonNullable<Adapter['getUserByAccount']> = async (account) => {
210
+ const result = await this.db
211
+ .select({
212
+ account: nextauthAccounts,
213
+ users,
214
+ })
215
+ .from(nextauthAccounts)
216
+ .innerJoin(users, eq(nextauthAccounts.userId, users.id))
217
+ .where(
218
+ and(
219
+ eq(nextauthAccounts.provider, account.provider),
220
+ eq(nextauthAccounts.providerAccountId, account.providerAccountId),
221
+ ),
222
+ )
223
+ .then((res: any) => res[0]);
224
+
225
+ return result?.users ? mapLobeUserToAdapterUser(result.users) : null;
226
+ };
227
+
228
+ getUserByEmail: NonNullable<Adapter['getUserByEmail']> = async (email) => {
229
+ const lobeUser =
230
+ email && typeof email === 'string' && email.trim()
231
+ ? await UserModel.findByEmail(this.db, email)
232
+ : undefined;
233
+ return lobeUser ? mapLobeUserToAdapterUser(lobeUser) : null;
234
+ };
235
+
236
+ linkAccount: NonNullable<Adapter['linkAccount']> = async (data) => {
237
+ const [account] = await this.db
238
+ .insert(nextauthAccounts)
239
+ .values(data as any)
240
+ .returning();
241
+ if (!account) throw new Error('NextAuthAccountModel: Failed to create account');
242
+ // TODO Update type annotation
243
+ return account as any;
244
+ };
245
+
246
+ listAuthenticatorsByUserId: NonNullable<Adapter['listAuthenticatorsByUserId']> = async (
247
+ userId,
248
+ ) => {
249
+ const result = await this.db
250
+ .select()
251
+ .from(nextauthAuthenticators)
252
+ .where(eq(nextauthAuthenticators.userId, userId))
253
+ .then((res: any) => res);
254
+ if (result.length === 0)
255
+ throw new Error('NextAuthUserService: Failed to get authenticator list');
256
+ return result.map((r: any) => mapAuthenticatorQueryResutlToAdapterAuthenticator(r));
257
+ };
258
+
259
+ unlinkAccount: NonNullable<Adapter['unlinkAccount']> = async (account) => {
260
+ await this.db
261
+ .delete(nextauthAccounts)
262
+ .where(
263
+ and(
264
+ eq(nextauthAccounts.provider, account.provider),
265
+ eq(nextauthAccounts.providerAccountId, account.providerAccountId),
266
+ ),
267
+ );
268
+ };
269
+
270
+ updateAuthenticatorCounter: NonNullable<Adapter['updateAuthenticatorCounter']> = async (
271
+ credentialID,
272
+ counter,
273
+ ) => {
274
+ const result = await this.db
275
+ .update(nextauthAuthenticators)
276
+ .set({ counter })
277
+ .where(eq(nextauthAuthenticators.credentialID, credentialID))
278
+ .returning()
279
+ .then((res: any) => res[0]);
280
+ if (!result) throw new Error('NextAuthUserService: Failed to update authenticator counter');
281
+ return mapAuthenticatorQueryResutlToAdapterAuthenticator(result);
282
+ };
283
+
284
+ updateSession: NonNullable<Adapter['updateSession']> = async (data) => {
285
+ const res = await this.db
286
+ .update(nextauthSessions)
287
+ .set(data)
288
+ .where(eq(nextauthSessions.sessionToken, data.sessionToken))
289
+ .returning();
290
+ return res[0];
291
+ };
292
+
293
+ updateUser: NonNullable<Adapter['updateUser']> = async (user) => {
294
+ const lobeUser = await UserModel.findById(this.db, user?.id);
295
+ if (!lobeUser) throw new Error('NextAuth: User not found');
296
+ const userModel = new UserModel(this.db, user.id);
297
+
298
+ const updatedUser = await userModel.updateUser({
299
+ ...partialMapAdapterUserToLobeUser(user),
300
+ });
301
+ if (!updatedUser) throw new Error('NextAuth: Failed to update user');
302
+
303
+ // merge new user data with old user data
304
+ const newAdapterUser = mapLobeUserToAdapterUser(lobeUser);
305
+ if (!newAdapterUser) {
306
+ throw new Error('NextAuth: Failed to map user data to adapter user');
307
+ }
308
+ return merge(newAdapterUser, user);
309
+ };
310
+
311
+ useVerificationToken: NonNullable<Adapter['useVerificationToken']> = async (identifier_token) => {
312
+ return await this.db
313
+ .delete(nextauthVerificationTokens)
314
+ .where(
315
+ and(
316
+ eq(nextauthVerificationTokens.identifier, identifier_token.identifier),
317
+ eq(nextauthVerificationTokens.token, identifier_token.token),
318
+ ),
319
+ )
320
+ .returning()
321
+ .then((res: any) => (res.length > 0 ? res[0] : null));
322
+ };
47
323
  }