@lobehub/chat 1.80.1 → 1.80.3

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/next.config.ts +5 -1
  4. package/package.json +1 -1
  5. package/src/app/[variants]/oauth/consent/[uid]/Client.tsx +36 -23
  6. package/src/app/[variants]/oauth/consent/[uid]/page.tsx +2 -0
  7. package/src/config/aiModels/azure.ts +79 -1
  8. package/src/config/aiModels/azureai.ts +181 -0
  9. package/src/config/aiModels/google.ts +36 -2
  10. package/src/config/aiModels/groq.ts +31 -3
  11. package/src/config/aiModels/hunyuan.ts +54 -18
  12. package/src/config/aiModels/moonshot.ts +17 -17
  13. package/src/config/aiModels/novita.ts +25 -30
  14. package/src/config/aiModels/siliconcloud.ts +80 -2
  15. package/src/config/aiModels/stepfun.ts +40 -31
  16. package/src/config/aiModels/tencentcloud.ts +7 -6
  17. package/src/config/aiModels/volcengine.ts +1 -0
  18. package/src/config/aiModels/zhipu.ts +91 -27
  19. package/src/const/settings/knowledge.ts +2 -2
  20. package/src/database/models/user.ts +13 -1
  21. package/src/layout/AuthProvider/NextAuth/UserUpdater.tsx +18 -11
  22. package/src/libs/oidc-provider/adapter.ts +5 -6
  23. package/src/libs/oidc-provider/config.ts +0 -3
  24. package/src/libs/oidc-provider/provider.ts +1 -0
  25. package/src/libs/trpc/edge/index.ts +0 -4
  26. package/src/libs/trpc/lambda/context.ts +90 -6
  27. package/src/libs/trpc/lambda/index.ts +2 -1
  28. package/src/libs/trpc/lambda/middleware/oidcAuth.ts +14 -0
  29. package/src/libs/trpc/middleware/userAuth.ts +2 -4
  30. package/src/server/routers/lambda/user.ts +9 -2
  31. package/src/server/services/oidc/index.ts +71 -0
  32. package/src/services/user/client.ts +5 -2
  33. package/src/store/user/slices/common/action.ts +9 -2
  34. package/src/types/user/index.ts +6 -1
  35. package/src/utils/parseModels.test.ts +19 -3
  36. package/src/utils/server/__tests__/auth.test.ts +45 -1
  37. package/src/utils/server/auth.ts +26 -2
@@ -8,7 +8,6 @@ const zhipuChatModels: AIChatModelCard[] = [
8
8
  contextWindowTokens: 16_384,
9
9
  description: 'GLM-Zero-Preview具备强大的复杂推理能力,在逻辑推理、数学、编程等领域表现优异。',
10
10
  displayName: 'GLM-Zero-Preview',
11
- enabled: true,
12
11
  id: 'glm-zero-preview',
13
12
  pricing: {
14
13
  currency: 'CNY',
@@ -17,6 +16,67 @@ const zhipuChatModels: AIChatModelCard[] = [
17
16
  },
18
17
  type: 'chat',
19
18
  },
19
+ {
20
+ abilities: {
21
+ reasoning: true,
22
+ search: true,
23
+ },
24
+ contextWindowTokens: 32_000,
25
+ description: '推理模型: 具备强大推理能力,适用于需要深度推理的任务。',
26
+ displayName: 'GLM-Z1-Air',
27
+ id: 'glm-z1-air',
28
+ maxOutput: 30_000,
29
+ pricing: {
30
+ currency: 'CNY',
31
+ input: 0.5,
32
+ output: 0.5,
33
+ },
34
+ settings: {
35
+ searchImpl: 'params',
36
+ },
37
+ type: 'chat',
38
+ },
39
+ {
40
+ abilities: {
41
+ reasoning: true,
42
+ search: true,
43
+ },
44
+ contextWindowTokens: 32_000,
45
+ description: '极速推理:具有超快的推理速度和强大的推理效果。',
46
+ displayName: 'GLM-Z1-AirX',
47
+ id: 'glm-z1-airx',
48
+ maxOutput: 30_000,
49
+ pricing: {
50
+ currency: 'CNY',
51
+ input: 5,
52
+ output: 5,
53
+ },
54
+ settings: {
55
+ searchImpl: 'params',
56
+ },
57
+ type: 'chat',
58
+ },
59
+ {
60
+ abilities: {
61
+ reasoning: true,
62
+ search: true,
63
+ },
64
+ contextWindowTokens: 32_000,
65
+ description: 'GLM-Z1 系列具备强大的复杂推理能力,在逻辑推理、数学、编程等领域表现优异。最大上下文长度为32K。',
66
+ displayName: 'GLM-Z1-Flash',
67
+ enabled: true,
68
+ id: 'glm-z1-flash',
69
+ maxOutput: 30_000,
70
+ pricing: {
71
+ currency: 'CNY',
72
+ input: 0,
73
+ output: 0,
74
+ },
75
+ settings: {
76
+ searchImpl: 'params',
77
+ },
78
+ type: 'chat',
79
+ },
20
80
  {
21
81
  abilities: {
22
82
  functionCall: true,
@@ -24,9 +84,10 @@ const zhipuChatModels: AIChatModelCard[] = [
24
84
  },
25
85
  contextWindowTokens: 128_000,
26
86
  description: 'GLM-4-Flash 是处理简单任务的理想选择,速度最快且免费。',
27
- displayName: 'GLM-4-Flash',
87
+ displayName: 'GLM-4-Flash-250414',
28
88
  enabled: true,
29
- id: 'glm-4-flash',
89
+ id: 'glm-4-flash-250414',
90
+ maxOutput: 4000,
30
91
  pricing: {
31
92
  currency: 'CNY',
32
93
  input: 0,
@@ -45,8 +106,8 @@ const zhipuChatModels: AIChatModelCard[] = [
45
106
  contextWindowTokens: 128_000,
46
107
  description: 'GLM-4-FlashX 是Flash的增强版本,超快推理速度。',
47
108
  displayName: 'GLM-4-FlashX',
48
- enabled: true,
49
109
  id: 'glm-4-flashx',
110
+ maxOutput: 4000,
50
111
  pricing: {
51
112
  currency: 'CNY',
52
113
  input: 0.1,
@@ -66,6 +127,7 @@ const zhipuChatModels: AIChatModelCard[] = [
66
127
  description: 'GLM-4-Long 支持超长文本输入,适合记忆型任务与大规模文档处理。',
67
128
  displayName: 'GLM-4-Long',
68
129
  id: 'glm-4-long',
130
+ maxOutput: 4000,
69
131
  pricing: {
70
132
  currency: 'CNY',
71
133
  input: 1,
@@ -81,15 +143,15 @@ const zhipuChatModels: AIChatModelCard[] = [
81
143
  functionCall: true,
82
144
  search: true,
83
145
  },
84
- contextWindowTokens: 128_000,
146
+ contextWindowTokens: 32_000,
85
147
  description: 'GLM-4-Air 是性价比高的版本,性能接近GLM-4,提供快速度和实惠的价格。',
86
- displayName: 'GLM-4-Air',
87
- enabled: true,
88
- id: 'glm-4-air',
148
+ displayName: 'GLM-4-Air-250414',
149
+ id: 'glm-4-air-250414',
150
+ maxOutput: 4000,
89
151
  pricing: {
90
152
  currency: 'CNY',
91
- input: 1,
92
- output: 1,
153
+ input: 0.5,
154
+ output: 0.5,
93
155
  },
94
156
  settings: {
95
157
  searchImpl: 'params',
@@ -104,8 +166,8 @@ const zhipuChatModels: AIChatModelCard[] = [
104
166
  contextWindowTokens: 8192,
105
167
  description: 'GLM-4-AirX 提供 GLM-4-Air 的高效版本,推理速度可达其2.6倍。',
106
168
  displayName: 'GLM-4-AirX',
107
- enabled: true,
108
169
  id: 'glm-4-airx',
170
+ maxOutput: 4000,
109
171
  pricing: {
110
172
  currency: 'CNY',
111
173
  input: 10,
@@ -144,8 +206,8 @@ const zhipuChatModels: AIChatModelCard[] = [
144
206
  contextWindowTokens: 128_000,
145
207
  description: 'GLM-4-Plus 作为高智能旗舰,具备强大的处理长文本和复杂任务的能力,性能全面提升。',
146
208
  displayName: 'GLM-4-Plus',
147
- enabled: true,
148
209
  id: 'glm-4-plus',
210
+ maxOutput: 4000,
149
211
  pricing: {
150
212
  currency: 'CNY',
151
213
  input: 50,
@@ -164,7 +226,7 @@ const zhipuChatModels: AIChatModelCard[] = [
164
226
  contextWindowTokens: 128_000,
165
227
  description: 'GLM-4-0520 是最新模型版本,专为高度复杂和多样化任务设计,表现卓越。',
166
228
  displayName: 'GLM-4-0520',
167
- id: 'glm-4-0520',
229
+ id: 'glm-4-0520', // 弃用时间 2025年12月30日
168
230
  pricing: {
169
231
  currency: 'CNY',
170
232
  input: 100,
@@ -183,7 +245,7 @@ const zhipuChatModels: AIChatModelCard[] = [
183
245
  contextWindowTokens: 128_000,
184
246
  description: 'GLM-4 是发布于2024年1月的旧旗舰版本,目前已被更强的 GLM-4-0520 取代。',
185
247
  displayName: 'GLM-4',
186
- id: 'glm-4',
248
+ id: 'glm-4', // 弃用时间 2025年6月30日
187
249
  pricing: {
188
250
  currency: 'CNY',
189
251
  input: 100,
@@ -198,7 +260,7 @@ const zhipuChatModels: AIChatModelCard[] = [
198
260
  abilities: {
199
261
  vision: true,
200
262
  },
201
- contextWindowTokens: 8192,
263
+ contextWindowTokens: 4096,
202
264
  description:
203
265
  'GLM-4V-Flash 专注于高效的单一图像理解,适用于快速图像解析的场景,例如实时图像分析或批量图像处理。',
204
266
  displayName: 'GLM-4V-Flash',
@@ -218,13 +280,12 @@ const zhipuChatModels: AIChatModelCard[] = [
218
280
  },
219
281
  contextWindowTokens: 8192,
220
282
  description: 'GLM-4V-Plus 具备对视频内容及多图片的理解能力,适合多模态任务。',
221
- displayName: 'GLM-4V-Plus',
222
- enabled: true,
223
- id: 'glm-4v-plus',
283
+ displayName: 'GLM-4V-Plus-0111',
284
+ id: 'glm-4v-plus-0111',
224
285
  pricing: {
225
286
  currency: 'CNY',
226
- input: 10,
227
- output: 10,
287
+ input: 4,
288
+ output: 4,
228
289
  },
229
290
  type: 'chat',
230
291
  },
@@ -232,7 +293,7 @@ const zhipuChatModels: AIChatModelCard[] = [
232
293
  abilities: {
233
294
  vision: true,
234
295
  },
235
- contextWindowTokens: 2048,
296
+ contextWindowTokens: 4096,
236
297
  description: 'GLM-4V 提供强大的图像理解与推理能力,支持多种视觉任务。',
237
298
  displayName: 'GLM-4V',
238
299
  id: 'glm-4v',
@@ -249,6 +310,7 @@ const zhipuChatModels: AIChatModelCard[] = [
249
310
  'CodeGeeX-4 是强大的AI编程助手,支持多种编程语言的智能问答与代码补全,提升开发效率。',
250
311
  displayName: 'CodeGeeX-4',
251
312
  id: 'codegeex-4',
313
+ maxOutput: 32_000,
252
314
  pricing: {
253
315
  currency: 'CNY',
254
316
  input: 0.1,
@@ -257,14 +319,15 @@ const zhipuChatModels: AIChatModelCard[] = [
257
319
  type: 'chat',
258
320
  },
259
321
  {
260
- contextWindowTokens: 4096,
261
- description: 'CharGLM-3 专为角色扮演与情感陪伴设计,支持超长多轮记忆与个性化对话,应用广泛。',
262
- displayName: 'CharGLM-3',
263
- id: 'charglm-3',
322
+ contextWindowTokens: 8192,
323
+ description: 'CharGLM-4 专为角色扮演与情感陪伴设计,支持超长多轮记忆与个性化对话,应用广泛。',
324
+ displayName: 'CharGLM-4',
325
+ id: 'charglm-4',
326
+ maxOutput: 4000,
264
327
  pricing: {
265
328
  currency: 'CNY',
266
- input: 15,
267
- output: 15,
329
+ input: 1,
330
+ output: 1,
268
331
  },
269
332
  type: 'chat',
270
333
  },
@@ -273,6 +336,7 @@ const zhipuChatModels: AIChatModelCard[] = [
273
336
  description: 'Emohaa 是心理模型,具备专业咨询能力,帮助用户理解情感问题。',
274
337
  displayName: 'Emohaa',
275
338
  id: 'emohaa',
339
+ maxOutput: 4000,
276
340
  pricing: {
277
341
  currency: 'CNY',
278
342
  input: 15,
@@ -2,7 +2,7 @@ import { FilesConfig, FilesConfigItem } from '@/types/user/settings/filesConfig'
2
2
 
3
3
  import {
4
4
  DEFAULT_EMBEDDING_MODEL,
5
- DEFAULT_PROVIDER,
5
+ DEFAULT_EMBEDDING_PROVIDER,
6
6
  DEFAULT_RERANK_MODEL,
7
7
  DEFAULT_RERANK_PROVIDER,
8
8
  DEFAULT_RERANK_QUERY_MODE,
@@ -10,7 +10,7 @@ import {
10
10
 
11
11
  export const DEFAULT_FILE_EMBEDDING_MODEL_ITEM: FilesConfigItem = {
12
12
  model: DEFAULT_EMBEDDING_MODEL,
13
- provider: DEFAULT_PROVIDER,
13
+ provider: DEFAULT_EMBEDDING_PROVIDER,
14
14
  };
15
15
 
16
16
  export const DEFAULT_FILE_RERANK_MODEL_ITEM: FilesConfigItem = {
@@ -62,10 +62,15 @@ export class UserModel {
62
62
  getUserState = async (decryptor: DecryptUserKeyVaults) => {
63
63
  const result = await this.db
64
64
  .select({
65
+ avatar: users.avatar,
66
+ email: users.email,
67
+ firstName: users.firstName,
68
+ fullName: users.fullName,
65
69
  isOnboarded: users.isOnboarded,
70
+ lastName: users.lastName,
66
71
  preference: users.preference,
67
-
68
72
  settingsDefaultAgent: userSettings.defaultAgent,
73
+
69
74
  settingsGeneral: userSettings.general,
70
75
  settingsHotkey: userSettings.hotkey,
71
76
  settingsKeyVaults: userSettings.keyVaults,
@@ -73,6 +78,7 @@ export class UserModel {
73
78
  settingsSystemAgent: userSettings.systemAgent,
74
79
  settingsTTS: userSettings.tts,
75
80
  settingsTool: userSettings.tool,
81
+ username: users.username,
76
82
  })
77
83
  .from(users)
78
84
  .where(eq(users.id, this.userId))
@@ -105,10 +111,16 @@ export class UserModel {
105
111
  };
106
112
 
107
113
  return {
114
+ avatar: state.avatar || undefined,
115
+ email: state.email || undefined,
116
+ firstName: state.firstName || undefined,
117
+ fullName: state.fullName || undefined,
108
118
  isOnboarded: state.isOnboarded,
119
+ lastName: state.lastName || undefined,
109
120
  preference: state.preference as UserPreference,
110
121
  settings,
111
122
  userId: this.userId,
123
+ username: state.username || undefined,
112
124
  };
113
125
  };
114
126
 
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useSession } from 'next-auth/react';
4
- import { memo } from 'react';
4
+ import { memo, useEffect } from 'react';
5
5
  import { createStoreUpdater } from 'zustand-utils';
6
6
 
7
7
  import { useUserStore } from '@/store/user';
@@ -17,20 +17,27 @@ const UserUpdater = memo(() => {
17
17
  const nextUser = session?.user;
18
18
  const useStoreUpdater = createStoreUpdater(useUserStore);
19
19
 
20
- const lobeUser = {
21
- avatar: nextUser?.image,
22
- email: nextUser?.email,
23
- fullName: nextUser?.name,
24
- id: nextUser?.id,
25
- } as LobeUser;
26
-
27
20
  useStoreUpdater('isLoaded', isLoaded);
28
- useStoreUpdater('user', lobeUser);
29
21
  useStoreUpdater('isSignedIn', isSignedIn);
30
-
31
22
  useStoreUpdater('nextSession', session);
32
- useStoreUpdater('nextUser', nextUser);
33
23
 
24
+ // 使用 useEffect 处理需要保持同步的用户数据
25
+ useEffect(() => {
26
+ if (nextUser) {
27
+ const userAvatar = useUserStore.getState().user?.avatar;
28
+
29
+ const lobeUser = {
30
+ // 头像使用设置的,而不是从 next-auth 中获取
31
+ avatar: userAvatar || '',
32
+ email: nextUser.email,
33
+ fullName: nextUser.name,
34
+ id: nextUser.id,
35
+ } as LobeUser;
36
+
37
+ // 更新用户相关数据
38
+ useUserStore.setState({ nextUser: nextUser, user: lobeUser });
39
+ }
40
+ }, [nextUser]);
34
41
  return null;
35
42
  });
36
43
 
@@ -22,9 +22,10 @@ class OIDCAdapter {
22
22
  private name: string;
23
23
 
24
24
  constructor(name: string, db: LobeChatDatabase) {
25
+ log('[%s] Constructor called with name: %s', name, name);
26
+
25
27
  this.name = name;
26
28
  this.db = db;
27
- log('Creating adapter for model: %s', name);
28
29
  }
29
30
 
30
31
  /**
@@ -530,12 +531,10 @@ class OIDCAdapter {
530
531
  /**
531
532
  * 创建适配器工厂
532
533
  */
533
- static createAdapterFactory(db: LobeChatDatabase) {
534
+ static createAdapterFactory = (db: LobeChatDatabase) => {
534
535
  log('Creating adapter factory with database instance');
535
- return function (name: string) {
536
- return new OIDCAdapter(name, db);
537
- };
538
- }
536
+ return (name: string) => new OIDCAdapter(name, db);
537
+ };
539
538
  }
540
539
 
541
540
  export { OIDCAdapter as DrizzleAdapter };
@@ -11,9 +11,6 @@ export const defaultClients: ClientMetadata[] = [
11
11
  // 仅支持授权码流程
12
12
  grant_types: ['authorization_code', 'refresh_token'],
13
13
 
14
- // 明确指明是原生应用
15
- isFirstParty: true,
16
-
17
14
  logo_uri: 'https://hub-apac-1.lobeobjects.space/lobehub-desktop-icon.png',
18
15
 
19
16
  // 桌面端注册的自定义协议回调(使用反向域名格式)
@@ -272,6 +272,7 @@ export const createOIDCProvider = async (db: LobeChatDatabase): Promise<Provider
272
272
  const baseUrl = urlJoin(appEnv.APP_URL!, '/oidc');
273
273
 
274
274
  const provider = new Provider(baseUrl, configuration);
275
+ provider.proxy = true;
275
276
 
276
277
  provider.on('server_error', (ctx, err) => {
277
278
  logProvider('OIDC Provider Server Error: %O', err); // Use logProvider
@@ -10,7 +10,6 @@
10
10
  import { DESKTOP_USER_ID } from '@/const/desktop';
11
11
  import { isDesktop } from '@/const/version';
12
12
 
13
- import { userAuth } from '../middleware/userAuth';
14
13
  import { edgeTrpc } from './init';
15
14
  import { jwtPayloadChecker } from './middleware/jwtPayload';
16
15
 
@@ -30,9 +29,6 @@ export const publicProcedure = edgeTrpc.procedure.use(({ next, ctx }) => {
30
29
  });
31
30
  });
32
31
 
33
- // procedure that asserts that the user is logged in
34
- export const authedProcedure = edgeTrpc.procedure.use(userAuth);
35
-
36
32
  // procedure that asserts that the user add the password
37
33
  export const passwordProcedure = edgeTrpc.procedure.use(jwtPayloadChecker);
38
34
 
@@ -1,14 +1,31 @@
1
+ import debug from 'debug';
1
2
  import { User } from 'next-auth';
2
3
  import { NextRequest } from 'next/server';
3
4
 
4
5
  import { JWTPayload, LOBE_CHAT_AUTH_HEADER, enableClerk, enableNextAuth } from '@/const/auth';
6
+ import { oidcEnv } from '@/envs/oidc';
5
7
  import { ClerkAuth, IClerkAuth } from '@/libs/clerk-auth';
8
+ import { extractBearerToken } from '@/utils/server/auth';
9
+
10
+ // Create context logger namespace
11
+ const log = debug('lobe-trpc:lambda:context');
12
+
13
+ export interface OIDCAuth {
14
+ // Other OIDC information that might be needed (optional, as payload contains all info)
15
+ [key: string]: any;
16
+ // OIDC token data (now the complete payload)
17
+ payload: any;
18
+ // User ID
19
+ sub: string;
20
+ }
6
21
 
7
22
  export interface AuthContext {
8
23
  authorizationHeader?: string | null;
9
24
  clerkAuth?: IClerkAuth;
10
25
  jwtPayload?: JWTPayload | null;
11
26
  nextAuth?: User;
27
+ // Add OIDC authentication information
28
+ oidcAuth?: OIDCAuth | null;
12
29
  userId?: string | null;
13
30
  }
14
31
 
@@ -20,13 +37,18 @@ export const createContextInner = async (params?: {
20
37
  authorizationHeader?: string | null;
21
38
  clerkAuth?: IClerkAuth;
22
39
  nextAuth?: User;
40
+ oidcAuth?: OIDCAuth | null;
23
41
  userId?: string | null;
24
- }): Promise<AuthContext> => ({
25
- authorizationHeader: params?.authorizationHeader,
26
- clerkAuth: params?.clerkAuth,
27
- nextAuth: params?.nextAuth,
28
- userId: params?.userId,
29
- });
42
+ }): Promise<AuthContext> => {
43
+ log('createContextInner called with params: %O', params);
44
+ return {
45
+ authorizationHeader: params?.authorizationHeader,
46
+ clerkAuth: params?.clerkAuth,
47
+ nextAuth: params?.nextAuth,
48
+ oidcAuth: params?.oidcAuth,
49
+ userId: params?.userId,
50
+ };
51
+ };
30
52
 
31
53
  export type LambdaContext = Awaited<ReturnType<typeof createContextInner>>;
32
54
 
@@ -35,23 +57,76 @@ export type LambdaContext = Awaited<ReturnType<typeof createContextInner>>;
35
57
  * @link https://trpc.io/docs/v11/context
36
58
  */
37
59
  export const createLambdaContext = async (request: NextRequest): Promise<LambdaContext> => {
60
+ log('createLambdaContext called for request');
38
61
  // for API-response caching see https://trpc.io/docs/v11/caching
39
62
 
40
63
  const authorization = request.headers.get(LOBE_CHAT_AUTH_HEADER);
64
+ log('LobeChat Authorization header: %s', authorization ? 'exists' : 'not found');
41
65
 
42
66
  let userId;
43
67
  let auth;
68
+ let oidcAuth = null;
69
+
70
+ // Prioritize checking the standard Authorization header for OIDC Bearer Token validation
71
+ if (oidcEnv.ENABLE_OIDC) {
72
+ log('OIDC enabled, attempting OIDC authentication');
73
+ const standardAuthorization = request.headers.get('Authorization');
74
+ log('Standard Authorization header: %s', standardAuthorization ? 'exists' : 'not found');
75
+
76
+ try {
77
+ // Use extractBearerToken from utils
78
+ const bearerToken = extractBearerToken(standardAuthorization);
79
+
80
+ log('Extracted Bearer Token: %s', bearerToken ? 'valid' : 'invalid');
81
+ if (bearerToken) {
82
+ const { OIDCService } = await import('@/server/services/oidc');
83
+
84
+ // Initialize OIDC service
85
+ log('Initializing OIDC service');
86
+ const oidcService = await OIDCService.initialize();
87
+ // Validate token using OIDCService
88
+ log('Validating OIDC token');
89
+ const tokenInfo = await oidcService.validateToken(bearerToken);
90
+ oidcAuth = {
91
+ payload: tokenInfo.tokenData,
92
+ ...tokenInfo.tokenData, // Spread payload into oidcAuth
93
+ sub: tokenInfo.userId, // Use tokenData as payload
94
+ };
95
+ userId = tokenInfo.userId;
96
+ log('OIDC authentication successful, userId: %s', userId);
97
+
98
+ // If OIDC authentication is successful, return context immediately
99
+ log('OIDC authentication successful, creating context and returning');
100
+ return createContextInner({
101
+ // Preserve original LobeChat Authorization Header (if any)
102
+ authorizationHeader: authorization,
103
+ oidcAuth,
104
+ userId,
105
+ });
106
+ }
107
+ } catch (error) {
108
+ // If OIDC authentication fails, log error and continue with other authentication methods
109
+ if (standardAuthorization?.startsWith('Bearer ')) {
110
+ log('OIDC authentication failed, error: %O', error);
111
+ console.error('OIDC authentication failed, trying other methods:', error);
112
+ }
113
+ }
114
+ }
44
115
 
116
+ // If OIDC is not enabled or validation fails, try LobeChat custom Header and other authentication methods
45
117
  if (enableClerk) {
118
+ log('Attempting Clerk authentication');
46
119
  const clerkAuth = new ClerkAuth();
47
120
  const result = clerkAuth.getAuthFromRequest(request);
48
121
  auth = result.clerkAuth;
49
122
  userId = result.userId;
123
+ log('Clerk authentication result, userId: %s', userId || 'not authenticated');
50
124
 
51
125
  return createContextInner({ authorizationHeader: authorization, clerkAuth: auth, userId });
52
126
  }
53
127
 
54
128
  if (enableNextAuth) {
129
+ log('Attempting NextAuth authentication');
55
130
  try {
56
131
  const { default: NextAuthEdge } = await import('@/libs/next-auth/edge');
57
132
 
@@ -59,12 +134,21 @@ export const createLambdaContext = async (request: NextRequest): Promise<LambdaC
59
134
  if (session && session?.user?.id) {
60
135
  auth = session.user;
61
136
  userId = session.user.id;
137
+ log('NextAuth authentication successful, userId: %s', userId);
138
+ } else {
139
+ log('NextAuth authentication failed, no valid session');
62
140
  }
63
141
  return createContextInner({ authorizationHeader: authorization, nextAuth: auth, userId });
64
142
  } catch (e) {
143
+ log('NextAuth authentication error: %O', e);
65
144
  console.error('next auth err', e);
66
145
  }
67
146
  }
68
147
 
148
+ // Final return, userId may be undefined
149
+ log(
150
+ 'All authentication methods attempted, returning final context, userId: %s',
151
+ userId || 'not authenticated',
152
+ );
69
153
  return createContextInner({ authorizationHeader: authorization, userId });
70
154
  };
@@ -12,6 +12,7 @@ import { isDesktop } from '@/const/version';
12
12
 
13
13
  import { userAuth } from '../middleware/userAuth';
14
14
  import { trpc } from './init';
15
+ import { oidcAuth } from './middleware/oidcAuth';
15
16
 
16
17
  /**
17
18
  * Create a router
@@ -30,7 +31,7 @@ export const publicProcedure = trpc.procedure.use(({ next, ctx }) => {
30
31
  });
31
32
 
32
33
  // procedure that asserts that the user is logged in
33
- export const authedProcedure = trpc.procedure.use(userAuth);
34
+ export const authedProcedure = trpc.procedure.use(oidcAuth).use(userAuth);
34
35
 
35
36
  /**
36
37
  * Create a server-side caller
@@ -0,0 +1,14 @@
1
+ import { trpc } from '../init';
2
+
3
+ export const oidcAuth = trpc.middleware(async (opts) => {
4
+ const { ctx, next } = opts;
5
+
6
+ // 检查 OIDC 认证
7
+ if (ctx.oidcAuth) {
8
+ return next({
9
+ ctx: { oidcAuth: ctx.oidcAuth, userId: ctx.oidcAuth.sub },
10
+ });
11
+ }
12
+
13
+ return next();
14
+ });
@@ -26,9 +26,7 @@ export const userAuth = trpc.middleware(async (opts) => {
26
26
  }
27
27
 
28
28
  return opts.next({
29
- ctx: {
30
- // user value is known to be non-null now
31
- userId: ctx.userId,
32
- },
29
+ // ✅ user value is known to be non-null now
30
+ ctx: { userId: ctx.userId },
33
31
  });
34
32
  });
@@ -85,17 +85,24 @@ export const userRouter = router({
85
85
  const hasExtraSession = await sessionModel.hasMoreThanN(1);
86
86
 
87
87
  return {
88
+ avatar: state.avatar,
88
89
  canEnablePWAGuide: hasMoreThan4Messages,
89
90
  canEnableTrace: hasMoreThan4Messages,
91
+ email: state.email,
92
+ firstName: state.firstName,
93
+
94
+ fullName: state.fullName,
95
+
90
96
  // 有消息,或者创建过助手,则认为有 conversation
91
97
  hasConversation: hasAnyMessages || hasExtraSession,
92
-
93
98
  // always return true for community version
94
99
  isOnboard: state.isOnboarded || true,
100
+ lastName: state.lastName,
95
101
  preference: state.preference as UserPreference,
96
102
  settings: state.settings,
97
103
  userId: ctx.userId,
98
- };
104
+ username: state.username,
105
+ } satisfies UserInitializationState;
99
106
  }),
100
107
 
101
108
  makeUserOnboarded: userProcedure.mutation(async ({ ctx }) => {