@lobehub/chat 1.122.3 → 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 +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/common.json +7 -0
- package/locales/bg-BG/common.json +7 -0
- package/locales/de-DE/common.json +7 -0
- package/locales/en-US/common.json +7 -0
- package/locales/es-ES/common.json +7 -0
- package/locales/fa-IR/common.json +7 -0
- package/locales/fr-FR/common.json +7 -0
- package/locales/it-IT/common.json +7 -0
- package/locales/ja-JP/common.json +7 -0
- package/locales/ko-KR/common.json +7 -0
- package/locales/nl-NL/common.json +7 -0
- package/locales/pl-PL/common.json +7 -0
- package/locales/pt-BR/common.json +7 -0
- package/locales/ru-RU/common.json +7 -0
- package/locales/tr-TR/common.json +7 -0
- package/locales/vi-VN/common.json +7 -0
- package/locales/zh-TW/common.json +7 -0
- package/package.json +1 -1
- package/packages/database/src/models/__tests__/drizzleMigration.test.ts +70 -0
- package/packages/database/src/models/__tests__/file.test.ts +57 -0
- package/packages/database/src/models/__tests__/session.test.ts +23 -1
- package/packages/database/src/server/models/__tests__/user.test.ts +76 -2
- 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/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +437 -0
- 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
@@ -0,0 +1,137 @@
|
|
1
|
+
import debug from 'debug';
|
2
|
+
import { NextRequest, NextResponse } from 'next/server';
|
3
|
+
|
4
|
+
import { serverDBEnv } from '@/config/db';
|
5
|
+
import { serverDB } from '@/database/server';
|
6
|
+
import { dateKeys } from '@/libs/next-auth/adapter';
|
7
|
+
import { NextAuthUserService } from '@/server/services/nextAuthUser';
|
8
|
+
|
9
|
+
const log = debug('lobe-next-auth:api:auth:adapter');
|
10
|
+
|
11
|
+
/**
|
12
|
+
* @description Process the db query for the NextAuth adapter.
|
13
|
+
* Returns the db query result directly and let NextAuth handle the raw results.
|
14
|
+
* @returns {
|
15
|
+
* success: boolean; // Only return false if the database query fails or the action is invalid.
|
16
|
+
* data?: any;
|
17
|
+
* error?: string;
|
18
|
+
* }
|
19
|
+
*/
|
20
|
+
export async function POST(req: NextRequest) {
|
21
|
+
try {
|
22
|
+
// try validate the request
|
23
|
+
if (
|
24
|
+
!req.headers.get('Authorization') ||
|
25
|
+
req.headers.get('Authorization')?.trim() !== `Bearer ${serverDBEnv.KEY_VAULTS_SECRET}`
|
26
|
+
) {
|
27
|
+
log('Unauthorized request, missing or invalid Authorization header');
|
28
|
+
return NextResponse.json({ error: 'Unauthorized', success: false }, { status: 401 });
|
29
|
+
}
|
30
|
+
|
31
|
+
// Parse the request body
|
32
|
+
const data = await req.json();
|
33
|
+
log('Received request data:', data);
|
34
|
+
// Preprocess
|
35
|
+
if (data?.data) {
|
36
|
+
for (const key of dateKeys) {
|
37
|
+
if (data?.data && data.data[key]) {
|
38
|
+
data.data[key] = new Date(data.data[key]);
|
39
|
+
continue;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
const service = new NextAuthUserService(serverDB);
|
44
|
+
let result;
|
45
|
+
switch (data.action) {
|
46
|
+
case 'createAuthenticator': {
|
47
|
+
result = await service.createAuthenticator(data.data);
|
48
|
+
break;
|
49
|
+
}
|
50
|
+
case 'createSession': {
|
51
|
+
result = await service.createSession(data.data);
|
52
|
+
break;
|
53
|
+
}
|
54
|
+
case 'createUser': {
|
55
|
+
result = await service.createUser(data.data);
|
56
|
+
break;
|
57
|
+
}
|
58
|
+
case 'createVerificationToken': {
|
59
|
+
result = await service.createVerificationToken(data.data);
|
60
|
+
break;
|
61
|
+
}
|
62
|
+
case 'deleteSession': {
|
63
|
+
result = await service.deleteSession(data.data);
|
64
|
+
break;
|
65
|
+
}
|
66
|
+
case 'deleteUser': {
|
67
|
+
result = await service.deleteUser(data.data);
|
68
|
+
break;
|
69
|
+
}
|
70
|
+
case 'getAccount': {
|
71
|
+
result = await service.getAccount(data.data.providerAccountId, data.data.provider);
|
72
|
+
break;
|
73
|
+
}
|
74
|
+
case 'getAuthenticator': {
|
75
|
+
result = await service.getAuthenticator(data.data);
|
76
|
+
break;
|
77
|
+
}
|
78
|
+
case 'getSessionAndUser': {
|
79
|
+
result = await service.getSessionAndUser(data.data);
|
80
|
+
break;
|
81
|
+
}
|
82
|
+
case 'getUser': {
|
83
|
+
result = await service.getUser(data.data);
|
84
|
+
break;
|
85
|
+
}
|
86
|
+
case 'getUserByAccount': {
|
87
|
+
result = await service.getUserByAccount(data.data);
|
88
|
+
break;
|
89
|
+
}
|
90
|
+
case 'getUserByEmail': {
|
91
|
+
result = await service.getUserByEmail(data.data);
|
92
|
+
break;
|
93
|
+
}
|
94
|
+
case 'linkAccount': {
|
95
|
+
result = await service.linkAccount(data.data);
|
96
|
+
break;
|
97
|
+
}
|
98
|
+
case 'listAuthenticatorsByUserId': {
|
99
|
+
result = await service.listAuthenticatorsByUserId(data.data);
|
100
|
+
break;
|
101
|
+
}
|
102
|
+
case 'unlinkAccount': {
|
103
|
+
result = await service.unlinkAccount(data.data);
|
104
|
+
break;
|
105
|
+
}
|
106
|
+
case 'updateAuthenticatorCounter': {
|
107
|
+
result = await service.updateAuthenticatorCounter(
|
108
|
+
data.data.credentialID,
|
109
|
+
data.data.counter,
|
110
|
+
);
|
111
|
+
break;
|
112
|
+
}
|
113
|
+
case 'updateSession': {
|
114
|
+
result = await service.updateSession(data.data);
|
115
|
+
break;
|
116
|
+
}
|
117
|
+
case 'updateUser': {
|
118
|
+
result = await service.updateUser(data.data);
|
119
|
+
break;
|
120
|
+
}
|
121
|
+
case 'useVerificationToken': {
|
122
|
+
result = await service.useVerificationToken(data.data);
|
123
|
+
break;
|
124
|
+
}
|
125
|
+
default: {
|
126
|
+
return NextResponse.json({ error: 'Invalid action', success: false }, { status: 400 });
|
127
|
+
}
|
128
|
+
}
|
129
|
+
return NextResponse.json({ data: result, success: true });
|
130
|
+
} catch (error) {
|
131
|
+
log('Error processing request:');
|
132
|
+
log(error);
|
133
|
+
return NextResponse.json({ error, success: false }, { status: 400 });
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
export const runtime = 'nodejs';
|
@@ -36,6 +36,15 @@ export const POST = async (req: Request): Promise<NextResponse> => {
|
|
36
36
|
},
|
37
37
|
);
|
38
38
|
}
|
39
|
+
case 'User.SuspensionStatus.Updated': {
|
40
|
+
if (data.isSuspended) {
|
41
|
+
return nextAuthUserService.safeSignOutUser({
|
42
|
+
provider: 'logto',
|
43
|
+
providerAccountId: data.id,
|
44
|
+
});
|
45
|
+
}
|
46
|
+
return NextResponse.json({ message: 'user reactivated', success: true }, { status: 200 });
|
47
|
+
}
|
39
48
|
|
40
49
|
default: {
|
41
50
|
pino.warn(
|
package/src/config/auth.ts
CHANGED
@@ -18,6 +18,8 @@ declare global {
|
|
18
18
|
|
19
19
|
NEXT_AUTH_DEBUG?: string;
|
20
20
|
|
21
|
+
NEXT_AUTH_SSO_SESSION_STRATEGY?: string;
|
22
|
+
|
21
23
|
AUTH0_CLIENT_ID?: string;
|
22
24
|
AUTH0_CLIENT_SECRET?: string;
|
23
25
|
AUTH0_ISSUER?: string;
|
@@ -159,6 +161,7 @@ export const getAuthConfig = () => {
|
|
159
161
|
NEXT_AUTH_SECRET: z.string().optional(),
|
160
162
|
NEXT_AUTH_SSO_PROVIDERS: z.string().optional().default('auth0'),
|
161
163
|
NEXT_AUTH_DEBUG: z.boolean().optional().default(false),
|
164
|
+
NEXT_AUTH_SSO_SESSION_STRATEGY: z.enum(['jwt', 'database']).optional().default('jwt'),
|
162
165
|
|
163
166
|
// Auth0
|
164
167
|
AUTH0_CLIENT_ID: z.string().optional(),
|
@@ -221,6 +224,7 @@ export const getAuthConfig = () => {
|
|
221
224
|
NEXT_AUTH_SSO_PROVIDERS: process.env.NEXT_AUTH_SSO_PROVIDERS,
|
222
225
|
NEXT_AUTH_SECRET: process.env.NEXT_AUTH_SECRET,
|
223
226
|
NEXT_AUTH_DEBUG: !!process.env.NEXT_AUTH_DEBUG,
|
227
|
+
NEXT_AUTH_SSO_SESSION_STRATEGY: process.env.NEXT_AUTH_SSO_SESSION_STRATEGY || 'jwt',
|
224
228
|
|
225
229
|
// Auth0
|
226
230
|
AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID,
|
@@ -4,273 +4,175 @@ import type {
|
|
4
4
|
AdapterUser,
|
5
5
|
VerificationToken,
|
6
6
|
} from '@auth/core/adapters';
|
7
|
-
import
|
8
|
-
import type { NeonDatabase } from 'drizzle-orm/neon-serverless';
|
7
|
+
import debug from 'debug';
|
9
8
|
import { Adapter, AdapterAccount } from 'next-auth/adapters';
|
9
|
+
import urlJoin from 'url-join';
|
10
10
|
|
11
|
-
import {
|
12
|
-
import
|
13
|
-
import { AgentService } from '@/server/services/agent';
|
14
|
-
import { merge } from '@/utils/merge';
|
11
|
+
import { serverDBEnv } from '@/config/db';
|
12
|
+
import { appEnv } from '@/envs/app';
|
15
13
|
|
16
|
-
|
17
|
-
mapAdapterUserToLobeUser,
|
18
|
-
mapAuthenticatorQueryResutlToAdapterAuthenticator,
|
19
|
-
mapLobeUserToAdapterUser,
|
20
|
-
partialMapAdapterUserToLobeUser,
|
21
|
-
} from './utils';
|
14
|
+
const log = debug('lobe-next-auth:adapter');
|
22
15
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
16
|
+
interface BackendAdapterResponse {
|
17
|
+
data?: any;
|
18
|
+
error?: string;
|
19
|
+
success: boolean;
|
20
|
+
}
|
21
|
+
|
22
|
+
// Due to use direct HTTP Post, the date string cannot parse automatically
|
23
|
+
export const dateKeys = ['expires', 'emailVerified'];
|
30
24
|
|
31
25
|
/**
|
32
26
|
* @description LobeNextAuthDbAdapter is implemented to handle the database operations
|
33
27
|
* for NextAuth, this function do the same things as `src/app/api/webhooks/clerk/route.ts`
|
34
28
|
* @returns {Adapter}
|
35
29
|
*/
|
36
|
-
export function LobeNextAuthDbAdapter(
|
30
|
+
export function LobeNextAuthDbAdapter(): Adapter {
|
31
|
+
const baseUrl = appEnv.APP_URL;
|
32
|
+
|
33
|
+
// Ensure the baseUrl is set, otherwise throw an error
|
34
|
+
if (!baseUrl) {
|
35
|
+
throw new Error('LobeNextAuthDbAdapter: APP_URL is not set in environment variables');
|
36
|
+
}
|
37
|
+
const interactionUrl = urlJoin(baseUrl, '/api/auth/adapter');
|
38
|
+
log(`LobeNextAuthDbAdapter initialized with url: ${interactionUrl}`);
|
39
|
+
|
40
|
+
// Ensure serverDBEnv.KEY_VAULTS_SECRET is set, otherwise throw an error
|
41
|
+
if (!serverDBEnv.KEY_VAULTS_SECRET) {
|
42
|
+
throw new Error('LobeNextAuthDbAdapter: KEY_VAULTS_SECRET is not set in environment variables');
|
43
|
+
}
|
44
|
+
|
45
|
+
const fetcher = (action: string, data: any) =>
|
46
|
+
fetch(interactionUrl, {
|
47
|
+
body: JSON.stringify({ action, data }),
|
48
|
+
headers: {
|
49
|
+
'Authorization': `Bearer ${serverDBEnv.KEY_VAULTS_SECRET}`,
|
50
|
+
'Content-Type': 'application/json',
|
51
|
+
},
|
52
|
+
method: 'POST',
|
53
|
+
});
|
54
|
+
const postProcessor = async (res: Response) => {
|
55
|
+
const data = (await res.json()) as BackendAdapterResponse;
|
56
|
+
log('LobeNextAuthDbAdapter: postProcessor called with data:', data);
|
57
|
+
if (!data.success) {
|
58
|
+
log('LobeNextAuthDbAdapter: Error in postProcessor:');
|
59
|
+
log(data);
|
60
|
+
throw new Error(`LobeNextAuthDbAdapter: ${data.error}`);
|
61
|
+
}
|
62
|
+
if (data?.data) {
|
63
|
+
for (const key of dateKeys) {
|
64
|
+
if (data.data[key]) {
|
65
|
+
data.data[key] = new Date(data.data[key]);
|
66
|
+
continue;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
return data.data;
|
71
|
+
};
|
72
|
+
|
37
73
|
return {
|
38
74
|
async createAuthenticator(authenticator): Promise<AdapterAuthenticator> {
|
39
|
-
const
|
40
|
-
|
41
|
-
.values(authenticator)
|
42
|
-
.returning()
|
43
|
-
.then((res) => res[0] ?? undefined);
|
44
|
-
if (!result) throw new Error('LobeNextAuthDbAdapter: Failed to create authenticator');
|
45
|
-
return mapAuthenticatorQueryResutlToAdapterAuthenticator(result);
|
75
|
+
const data = await fetcher('createAuthenticator', authenticator);
|
76
|
+
return await postProcessor(data);
|
46
77
|
},
|
47
|
-
async createSession(
|
48
|
-
|
49
|
-
|
50
|
-
.values(data)
|
51
|
-
.returning()
|
52
|
-
.then((res) => res[0]);
|
78
|
+
async createSession(session): Promise<AdapterSession> {
|
79
|
+
const data = await fetcher('createSession', session);
|
80
|
+
return await postProcessor(data);
|
53
81
|
},
|
54
82
|
async createUser(user): Promise<AdapterUser> {
|
55
|
-
const
|
56
|
-
|
57
|
-
let existingUser =
|
58
|
-
email && typeof email === 'string' && email.trim()
|
59
|
-
? await UserModel.findByEmail(serverDB, email)
|
60
|
-
: undefined;
|
61
|
-
// If the user is not found by email, try to find by providerAccountId
|
62
|
-
if (!existingUser && providerAccountId) {
|
63
|
-
existingUser = await UserModel.findById(serverDB, providerAccountId);
|
64
|
-
}
|
65
|
-
if (existingUser) {
|
66
|
-
const adapterUser = mapLobeUserToAdapterUser(existingUser);
|
67
|
-
return adapterUser;
|
68
|
-
}
|
69
|
-
|
70
|
-
// create a new user if it does not exist
|
71
|
-
// Use id from provider if it exists, otherwise use id assigned by next-auth
|
72
|
-
// ref: https://github.com/lobehub/lobe-chat/pull/2935
|
73
|
-
const uid = providerAccountId ?? id;
|
74
|
-
await UserModel.createUser(
|
75
|
-
serverDB,
|
76
|
-
mapAdapterUserToLobeUser({
|
77
|
-
email,
|
78
|
-
emailVerified,
|
79
|
-
// Use providerAccountId as userid to identify if the user exists in a SSO provider
|
80
|
-
id: uid,
|
81
|
-
image,
|
82
|
-
name,
|
83
|
-
}),
|
84
|
-
);
|
85
|
-
|
86
|
-
// 3. Create an inbox session for the user
|
87
|
-
const agentService = new AgentService(serverDB, uid);
|
88
|
-
await agentService.createInbox();
|
89
|
-
|
90
|
-
return { ...user, id: uid };
|
83
|
+
const data = await fetcher('createUser', user);
|
84
|
+
return await postProcessor(data);
|
91
85
|
},
|
92
86
|
async createVerificationToken(data): Promise<VerificationToken | null | undefined> {
|
93
|
-
|
94
|
-
|
95
|
-
.values(data)
|
96
|
-
.returning()
|
97
|
-
.then((res) => res[0]);
|
87
|
+
const result = await fetcher('createVerificationToken', data);
|
88
|
+
return await postProcessor(result);
|
98
89
|
},
|
99
90
|
async deleteSession(sessionToken): Promise<AdapterSession | null | undefined> {
|
100
|
-
await
|
101
|
-
|
102
|
-
.where(eq(nextauthSessions.sessionToken, sessionToken));
|
91
|
+
const result = await fetcher('deleteSession', sessionToken);
|
92
|
+
await postProcessor(result);
|
103
93
|
return;
|
104
94
|
},
|
105
95
|
async deleteUser(id): Promise<AdapterUser | null | undefined> {
|
106
|
-
const
|
107
|
-
|
108
|
-
|
109
|
-
await UserModel.deleteUser(serverDB, id);
|
96
|
+
const result = await fetcher('deleteUser', id);
|
97
|
+
await postProcessor(result);
|
110
98
|
return;
|
111
99
|
},
|
112
100
|
|
113
101
|
async getAccount(providerAccountId, provider): Promise<AdapterAccount | null> {
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
eq(nextauthAccounts.provider, provider),
|
120
|
-
eq(nextauthAccounts.providerAccountId, providerAccountId),
|
121
|
-
),
|
122
|
-
)
|
123
|
-
.then((res) => res[0] ?? null) as Promise<AdapterAccount | null>;
|
102
|
+
const data = await fetcher('getAccount', {
|
103
|
+
provider,
|
104
|
+
providerAccountId,
|
105
|
+
});
|
106
|
+
return await postProcessor(data);
|
124
107
|
},
|
125
108
|
|
126
109
|
async getAuthenticator(credentialID): Promise<AdapterAuthenticator | null> {
|
127
|
-
const result = await
|
128
|
-
|
129
|
-
.from(nextauthAuthenticators)
|
130
|
-
.where(eq(nextauthAuthenticators.credentialID, credentialID))
|
131
|
-
.then((res) => res[0] ?? null);
|
132
|
-
if (!result) throw new Error('LobeNextAuthDbAdapter: Failed to get authenticator');
|
133
|
-
return mapAuthenticatorQueryResutlToAdapterAuthenticator(result);
|
110
|
+
const result = await fetcher('getAuthenticator', credentialID);
|
111
|
+
return await postProcessor(result);
|
134
112
|
},
|
135
113
|
|
136
114
|
async getSessionAndUser(sessionToken): Promise<{
|
137
115
|
session: AdapterSession;
|
138
116
|
user: AdapterUser;
|
139
117
|
} | null> {
|
140
|
-
const result = await
|
141
|
-
|
142
|
-
session: nextauthSessions,
|
143
|
-
user: users,
|
144
|
-
})
|
145
|
-
.from(nextauthSessions)
|
146
|
-
.where(eq(nextauthSessions.sessionToken, sessionToken))
|
147
|
-
.innerJoin(users, eq(users.id, nextauthSessions.userId))
|
148
|
-
.then((res) => (res.length > 0 ? res[0] : null));
|
149
|
-
|
150
|
-
if (!result) return null;
|
151
|
-
const adapterUser = mapLobeUserToAdapterUser(result.user);
|
152
|
-
if (!adapterUser) return null;
|
153
|
-
return {
|
154
|
-
session: result.session,
|
155
|
-
user: adapterUser,
|
156
|
-
};
|
118
|
+
const result = await fetcher('getSessionAndUser', sessionToken);
|
119
|
+
return await postProcessor(result);
|
157
120
|
},
|
158
121
|
|
159
122
|
async getUser(id): Promise<AdapterUser | null> {
|
160
|
-
|
161
|
-
|
162
|
-
return
|
123
|
+
log('getUser called with id:', id);
|
124
|
+
const result = await fetcher('getUser', id);
|
125
|
+
return await postProcessor(result);
|
163
126
|
},
|
164
127
|
|
165
128
|
async getUserByAccount(account): Promise<AdapterUser | null> {
|
166
|
-
const
|
167
|
-
|
168
|
-
account: nextauthAccounts,
|
169
|
-
users,
|
170
|
-
})
|
171
|
-
.from(nextauthAccounts)
|
172
|
-
.innerJoin(users, eq(nextauthAccounts.userId, users.id))
|
173
|
-
.where(
|
174
|
-
and(
|
175
|
-
eq(nextauthAccounts.provider, account.provider),
|
176
|
-
eq(nextauthAccounts.providerAccountId, account.providerAccountId),
|
177
|
-
),
|
178
|
-
)
|
179
|
-
.then((res) => res[0]);
|
180
|
-
|
181
|
-
return result?.users ? mapLobeUserToAdapterUser(result.users) : null;
|
129
|
+
const data = await fetcher('getUserByAccount', account);
|
130
|
+
return await postProcessor(data);
|
182
131
|
},
|
183
132
|
|
184
133
|
async getUserByEmail(email): Promise<AdapterUser | null> {
|
185
|
-
const
|
186
|
-
|
187
|
-
? await UserModel.findByEmail(serverDB, email)
|
188
|
-
: undefined;
|
189
|
-
return lobeUser ? mapLobeUserToAdapterUser(lobeUser) : null;
|
134
|
+
const data = await fetcher('getUserByEmail', email);
|
135
|
+
return await postProcessor(data);
|
190
136
|
},
|
191
137
|
|
192
138
|
async linkAccount(data): Promise<AdapterAccount | null | undefined> {
|
193
|
-
const
|
194
|
-
|
195
|
-
.values(data as any)
|
196
|
-
.returning();
|
197
|
-
if (!account) throw new Error('NextAuthAccountModel: Failed to create account');
|
198
|
-
// TODO Update type annotation
|
199
|
-
return account as any;
|
139
|
+
const result = await fetcher('linkAccount', data);
|
140
|
+
return await postProcessor(result);
|
200
141
|
},
|
201
142
|
|
202
143
|
async listAuthenticatorsByUserId(userId): Promise<AdapterAuthenticator[]> {
|
203
|
-
const result = await
|
204
|
-
|
205
|
-
.from(nextauthAuthenticators)
|
206
|
-
.where(eq(nextauthAuthenticators.userId, userId))
|
207
|
-
.then((res) => res);
|
208
|
-
if (result.length === 0)
|
209
|
-
throw new Error('LobeNextAuthDbAdapter: Failed to get authenticator list');
|
210
|
-
return result.map((r) => mapAuthenticatorQueryResutlToAdapterAuthenticator(r));
|
144
|
+
const result = await fetcher('listAuthenticatorsByUserId', userId);
|
145
|
+
return await postProcessor(result);
|
211
146
|
},
|
212
147
|
|
213
148
|
// @ts-ignore: The return type is {Promise<void> | Awaitable<AdapterAccount | undefined>}
|
214
149
|
async unlinkAccount(account): Promise<void | AdapterAccount | undefined> {
|
215
|
-
await
|
216
|
-
|
217
|
-
|
218
|
-
and(
|
219
|
-
eq(nextauthAccounts.provider, account.provider),
|
220
|
-
eq(nextauthAccounts.providerAccountId, account.providerAccountId),
|
221
|
-
),
|
222
|
-
);
|
150
|
+
const result = await fetcher('unlinkAccount', account);
|
151
|
+
await postProcessor(result);
|
152
|
+
return;
|
223
153
|
},
|
224
154
|
|
225
155
|
async updateAuthenticatorCounter(credentialID, counter): Promise<AdapterAuthenticator> {
|
226
|
-
const result = await
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
.then((res) => res[0]);
|
232
|
-
if (!result) throw new Error('LobeNextAuthDbAdapter: Failed to update authenticator counter');
|
233
|
-
return mapAuthenticatorQueryResutlToAdapterAuthenticator(result);
|
156
|
+
const result = await fetcher('updateAuthenticatorCounter', {
|
157
|
+
counter,
|
158
|
+
credentialID,
|
159
|
+
});
|
160
|
+
return await postProcessor(result);
|
234
161
|
},
|
235
162
|
|
236
163
|
async updateSession(data): Promise<AdapterSession | null | undefined> {
|
237
|
-
const
|
238
|
-
|
239
|
-
.set(data)
|
240
|
-
.where(eq(nextauthSessions.sessionToken, data.sessionToken))
|
241
|
-
.returning();
|
242
|
-
return res[0];
|
164
|
+
const result = await fetcher('updateSession', data);
|
165
|
+
return await postProcessor(result);
|
243
166
|
},
|
244
167
|
|
245
168
|
async updateUser(user): Promise<AdapterUser> {
|
246
|
-
const
|
247
|
-
|
248
|
-
const userModel = new UserModel(serverDB, user.id);
|
249
|
-
|
250
|
-
const updatedUser = await userModel.updateUser({
|
251
|
-
...partialMapAdapterUserToLobeUser(user),
|
252
|
-
});
|
253
|
-
if (!updatedUser) throw new Error('NextAuth: Failed to update user');
|
254
|
-
|
255
|
-
// merge new user data with old user data
|
256
|
-
const newAdapterUser = mapLobeUserToAdapterUser(lobeUser);
|
257
|
-
if (!newAdapterUser) {
|
258
|
-
throw new Error('NextAuth: Failed to map user data to adapter user');
|
259
|
-
}
|
260
|
-
return merge(newAdapterUser, user);
|
169
|
+
const result = await fetcher('updateUser', user);
|
170
|
+
return await postProcessor(result);
|
261
171
|
},
|
262
172
|
|
263
173
|
async useVerificationToken(identifier_token): Promise<VerificationToken | null> {
|
264
|
-
|
265
|
-
|
266
|
-
.where(
|
267
|
-
and(
|
268
|
-
eq(nextauthVerificationTokens.identifier, identifier_token.identifier),
|
269
|
-
eq(nextauthVerificationTokens.token, identifier_token.token),
|
270
|
-
),
|
271
|
-
)
|
272
|
-
.returning()
|
273
|
-
.then((res) => (res.length > 0 ? res[0] : null));
|
174
|
+
const result = await fetcher('useVerificationToken', identifier_token);
|
175
|
+
return await postProcessor(result);
|
274
176
|
},
|
275
177
|
};
|
276
178
|
}
|
@@ -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
|
9
|
-
?
|
10
|
-
|
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
|
-
|
20
|
+
if (validProvider) return validProvider.provider;
|
13
21
|
|
14
|
-
|
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:
|
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:
|
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
|
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
|
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
|
17
|
-
*
|
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:
|
57
|
+
const { default: NextAuth } = await import('@/libs/next-auth');
|
58
58
|
|
59
|
-
const session = await
|
59
|
+
const session = await NextAuth.auth();
|
60
60
|
if (session && session?.user?.id) {
|
61
61
|
auth = session.user;
|
62
62
|
userId = session.user.id;
|