@lobehub/chat 1.100.1 → 1.100.2

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 CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.100.2](https://github.com/lobehub/lobe-chat/compare/v1.100.1...v1.100.2)
6
+
7
+ <sup>Released on **2025-07-18**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix webapi proxy with clerk.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix webapi proxy with clerk, closes [#8479](https://github.com/lobehub/lobe-chat/issues/8479) ([7dd65f0](https://github.com/lobehub/lobe-chat/commit/7dd65f0))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 1.100.1](https://github.com/lobehub/lobe-chat/compare/v1.100.0...v1.100.1)
6
31
 
7
32
  <sup>Released on **2025-07-17**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Fix webapi proxy with clerk."
6
+ ]
7
+ },
8
+ "date": "2025-07-18",
9
+ "version": "1.100.2"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "fixes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.100.1",
3
+ "version": "1.100.2",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -1,9 +1,16 @@
1
1
  import { AuthObject } from '@clerk/backend';
2
2
  import { NextRequest } from 'next/server';
3
3
 
4
- import { JWTPayload, LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED, enableClerk } from '@/const/auth';
4
+ import {
5
+ JWTPayload,
6
+ LOBE_CHAT_AUTH_HEADER,
7
+ LOBE_CHAT_OIDC_AUTH_HEADER,
8
+ OAUTH_AUTHORIZED,
9
+ enableClerk,
10
+ } from '@/const/auth';
5
11
  import { ClerkAuth } from '@/libs/clerk-auth';
6
12
  import { AgentRuntime, AgentRuntimeError, ChatCompletionErrorPayload } from '@/libs/model-runtime';
13
+ import { validateOIDCJWT } from '@/libs/oidc-provider/jwt';
7
14
  import { ChatErrorType } from '@/types/fetch';
8
15
  import { createErrorResponse } from '@/utils/errorResponse';
9
16
  import { getJWTPayload } from '@/utils/server/jwt';
@@ -50,12 +57,26 @@ export const checkAuth =
50
57
 
51
58
  jwtPayload = await getJWTPayload(authorization);
52
59
 
53
- checkAuthMethod({
54
- accessCode: jwtPayload.accessCode,
55
- apiKey: jwtPayload.apiKey,
56
- clerkAuth,
57
- nextAuthAuthorized: oauthAuthorized,
58
- });
60
+ const oidcAuthorization = req.headers.get(LOBE_CHAT_OIDC_AUTH_HEADER);
61
+ let isUseOidcAuth = false;
62
+ if (!!oidcAuthorization) {
63
+ const oidc = await validateOIDCJWT(oidcAuthorization);
64
+
65
+ isUseOidcAuth = true;
66
+
67
+ jwtPayload = {
68
+ ...jwtPayload,
69
+ userId: oidc.userId,
70
+ };
71
+ }
72
+
73
+ if (!isUseOidcAuth)
74
+ checkAuthMethod({
75
+ accessCode: jwtPayload.accessCode,
76
+ apiKey: jwtPayload.apiKey,
77
+ clerkAuth,
78
+ nextAuthAuthorized: oauthAuthorized,
79
+ });
59
80
  } catch (e) {
60
81
  const params = await options.params;
61
82
 
package/src/const/auth.ts CHANGED
@@ -5,6 +5,7 @@ export const enableNextAuth = authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH;
5
5
  export const enableAuth = enableClerk || enableNextAuth || false;
6
6
 
7
7
  export const LOBE_CHAT_AUTH_HEADER = 'X-lobe-chat-auth';
8
+ export const LOBE_CHAT_OIDC_AUTH_HEADER = 'Oidc-Auth';
8
9
 
9
10
  export const OAUTH_AUTHORIZED = 'X-oauth-authorized';
10
11
 
@@ -41,10 +41,7 @@ export const getJWKS = (): object => {
41
41
  }
42
42
  };
43
43
 
44
- /**
45
- * 从环境变量中获取 JWKS 并提取第一个 RSA 密钥
46
- */
47
- const getJWKSPublicKey = async () => {
44
+ const getVerificationKey = async () => {
48
45
  try {
49
46
  const jwksString = oidcEnv.OIDC_JWKS_KEY;
50
47
 
@@ -58,20 +55,32 @@ const getJWKSPublicKey = async () => {
58
55
  throw new Error('JWKS 格式无效: 缺少或为空的 keys 数组');
59
56
  }
60
57
 
61
- // 查找 RS256 算法的 RSA 密钥
62
- const rsaKey = jwks.keys.find((key: any) => key.alg === 'RS256' && key.kty === 'RSA');
63
-
64
- if (!rsaKey) {
58
+ const privateRsaKey = jwks.keys.find((key: any) => key.alg === 'RS256' && key.kty === 'RSA');
59
+ if (!privateRsaKey) {
65
60
  throw new Error('JWKS 中没有找到 RS256 算法的 RSA 密钥');
66
61
  }
67
62
 
68
- // 导入 JWK 为公钥
69
- const publicKey = await importJWK(rsaKey, 'RS256');
63
+ // 创建一个只包含公钥组件的“纯净”JWK对象。
64
+ // RSA公钥的关键字段是 kty, n, e。其他如 kid, alg, use 也是公共的。
65
+ const publicKeyJwk = {
66
+ alg: privateRsaKey.alg,
67
+ e: privateRsaKey.e,
68
+ kid: privateRsaKey.kid,
69
+ kty: privateRsaKey.kty,
70
+ n: privateRsaKey.n,
71
+ use: privateRsaKey.use,
72
+ };
73
+
74
+ // 移除任何可能存在的 undefined 字段,保持对象干净
75
+ Object.keys(publicKeyJwk).forEach(
76
+ (key) => (publicKeyJwk as any)[key] === undefined && delete (publicKeyJwk as any)[key],
77
+ );
70
78
 
71
- return publicKey;
79
+ // 现在,无论在哪个环境下,`importJWK` 都会将这个对象正确地识别为一个公钥。
80
+ return await importJWK(publicKeyJwk, 'RS256');
72
81
  } catch (error) {
73
82
  log('获取 JWKS 公钥失败: %O', error);
74
- throw new Error(`JWKS 公钥获取失败: ${(error as Error).message}`);
83
+ throw new Error(`JWKS 公key获取失败: ${(error as Error).message}`);
75
84
  }
76
85
  };
77
86
 
@@ -85,7 +94,7 @@ export const validateOIDCJWT = async (token: string) => {
85
94
  log('开始验证 OIDC JWT token');
86
95
 
87
96
  // 获取公钥
88
- const publicKey = await getJWKSPublicKey();
97
+ const publicKey = await getVerificationKey();
89
98
 
90
99
  // 验证 JWT
91
100
  const { payload } = await jwtVerify(token, publicKey, {
@@ -3,7 +3,13 @@ import debug from 'debug';
3
3
  import { User } from 'next-auth';
4
4
  import { NextRequest } from 'next/server';
5
5
 
6
- import { JWTPayload, LOBE_CHAT_AUTH_HEADER, enableClerk, enableNextAuth } from '@/const/auth';
6
+ import {
7
+ JWTPayload,
8
+ LOBE_CHAT_AUTH_HEADER,
9
+ LOBE_CHAT_OIDC_AUTH_HEADER,
10
+ enableClerk,
11
+ enableNextAuth,
12
+ } from '@/const/auth';
7
13
  import { oidcEnv } from '@/envs/oidc';
8
14
  import { ClerkAuth, IClerkAuth } from '@/libs/clerk-auth';
9
15
  import { validateOIDCJWT } from '@/libs/oidc-provider/jwt';
@@ -102,7 +108,7 @@ export const createLambdaContext = async (request: NextRequest): Promise<LambdaC
102
108
  if (oidcEnv.ENABLE_OIDC) {
103
109
  log('OIDC enabled, attempting OIDC authentication');
104
110
  const standardAuthorization = request.headers.get('Authorization');
105
- const oidcAuthToken = request.headers.get('Oidc-Auth');
111
+ const oidcAuthToken = request.headers.get(LOBE_CHAT_OIDC_AUTH_HEADER);
106
112
  log('Standard Authorization header: %s', standardAuthorization ? 'exists' : 'not found');
107
113
  log('Oidc-Auth header: %s', oidcAuthToken ? 'exists' : 'not found');
108
114