Skip to content

Conversation

arvinxx
Copy link
Member

@arvinxx arvinxx commented Jul 29, 2025

💻 变更类型 | Change Type

  • ✨ feat
  • 🐛 fix
  • ♻️ refactor
  • 💄 style
  • 👷 build
  • ⚡️ perf
  • 📝 docs
  • 🔨 chore

🔀 变更说明 | Description of Change

重构客户端 LOBE_CHAT_AUTH_HEADER 生成逻辑的理由和实现细节。旧的实现方式使用 jose 库进行 JWT 签名(JWS),现已被一个基于 XOR 和 Base64 编码的轻量级载荷混淆机制所取代。此变更解决了前端构建问题,并专为在纯 HTTP 环境下自部署的用户提供一层基础的数据混淆,同时避免了引入真正加密带来的复杂性。

📝 补充信息 | Additional Information

旧实现存在两个主要问题:

  1. 客户端构建失败: jose 库在进行签名操作时依赖 Node.js 核心模块(例如 node:buffer),这导致在现代前端框架如 Next.js 中,把该逻辑纳入客户端打包时构建失败,因为浏览器环境中不存在这些模块。

  2. JWS 的安全错觉: 使用 SignJWT(JWS)仅对负载进行了签名,未加密。载荷中包含敏感的用户 API 密钥,实际上只是经过 Base64 编码,任何截获流量的人都能轻易读取,造成了虚假的安全感。

业务上的主要限制是要支持在内部基于 IP 的网络环境中自部署应用且无 HTTPS 的用户。在此类环境下,无法保证端到端的真正安全,但需要一个简单的机制以防止敏感数据被随意观察。

新方案用两步的混淆机制替代了 JWT 签名流程:

  1. XOR 混淆: 对含敏感数据的 JSON 载荷进行字节级的 XOR 操作,使用一个硬编码的共享密钥(SECRET_XOR_KEY)。该密钥在客户端代码和服务器端代码中均有配置。

  2. Base64 编码: 将 XOR 结果(字节数组)编码为 Base64 字符串,确保生成的内容为可打印的 ASCII 字符,方便通过 HTTP 头部安全传输。

服务器端 API 接口将反向执行该流程:先 Base64 解码头部内容,再用相同的 XOR 密钥解混淆,恢复原始 JSON 载荷。

该方案是低复杂度的务实措施,用以阻止随意窥视。

Summary by Sourcery

Refactor authentication flow to replace JWT-based payload encoding with XOR obfuscation and update related utilities, middleware, routes, and tests accordingly

Enhancements:

  • Add client and server utilities for XOR-based payload obfuscation and de-obfuscation using Base64 encoding
  • Update auth service, middleware, and route handlers to use obfuscatePayloadWithXOR/getXorPayload instead of JWT utilities
  • Remove legacy JWT utilities and tests across the codebase
  • Update tests to mock and assert getXorPayload instead of getJWTPayload

Build:

  • Upgrade jose and next dependencies to newer versions

Copy link

vercel bot commented Jul 29, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
lobe-chat-database ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 29, 2025 6:42am
lobe-chat-preview ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 29, 2025 6:42am

Copy link
Contributor

sourcery-ai bot commented Jul 29, 2025

Reviewer's Guide

This refactor replaces JWT-based payload encoding/decoding with a custom XOR-based obfuscation mechanism by introducing new client/server XOR utilities, updating all service/middleware calls and tests accordingly, removing the old JWT utilities, and bumping relevant dependencies.

Sequence diagram for payload obfuscation and de-obfuscation (XOR-based)

sequenceDiagram
    participant Client
    participant Server
    participant XORClient as obfuscatePayloadWithXOR
    participant XORServer as getXorPayload

    Client->>XORClient: obfuscatePayloadWithXOR(payload)
    XORClient-->>Client: obfuscatedToken (Base64)
    Client->>Server: Send obfuscatedToken in Authorization header
    Server->>XORServer: getXorPayload(obfuscatedToken)
    XORServer-->>Server: JWTPayload (decoded)
Loading

Class diagram for XOR obfuscation utilities (client/server)

classDiagram
    class obfuscatePayloadWithXOR {
        +obfuscatePayloadWithXOR<T>(payload: T): string
    }
    class getXorPayload {
        +getXorPayload(token: string): JWTPayload
    }
    class JWTPayload {
        +accessCode: string
        +apiKey: string
        +azureApiVersion: string
        +userId: string
        ...
    }
    obfuscatePayloadWithXOR --|> JWTPayload : obfuscates
    getXorPayload --|> JWTPayload : decodes
Loading

Class diagram for removed JWT utilities

classDiagram
    class createJWT {
        +createJWT<T>(payload: T): string
    }
    class getJWTPayload {
        +getJWTPayload(token: string): JWTPayload
    }
    class JWTPayload {
        +accessCode: string
        +apiKey: string
        +azureApiVersion: string
        +userId: string
        ...
    }
    createJWT --|> JWTPayload : encodes
    getJWTPayload --|> JWTPayload : decodes
Loading

Class diagram for updated auth constants

classDiagram
    class auth {
        +SECRET_XOR_KEY: string
        -JWT_SECRET_KEY (removed)
        -NON_HTTP_PREFIX (removed)
    }
Loading

File-Level Changes

Change Details Files
Introduce XOR obfuscation utilities
  • Add server-side getXorPayload to decode Base64+XOR payloads
  • Add client-side obfuscatePayloadWithXOR to encode payloads
  • Include unit tests for both XOR utilities
src/utils/server/xor.ts
src/utils/client/xor-obfuscation.ts
src/utils/client/xor-obfuscation.test.ts
src/utils/server/xor.test.ts
Replace JWT functions with XOR payload functions
  • Swap getJWTPayload calls for getXorPayload in middleware, routes, and tests
  • Swap createJWT for obfuscatePayloadWithXOR in auth service
src/app/(backend)/webapi/chat/[provider]/route.test.ts
src/app/(backend)/middleware/auth/index.test.ts
src/app/(backend)/middleware/auth/index.ts
src/app/(backend)/webapi/plugin/gateway/route.ts
src/services/_auth.ts
src/libs/trpc/edge/middleware/jwtPayload.ts
src/libs/trpc/edge/middleware/jwtPayload.test.ts
src/libs/trpc/lambda/middleware/keyVaults.ts
Remove old JWT utilities and tests
  • Delete jwt.ts implementations from client and server
  • Remove associated jwt utility test files
src/utils/jwt.ts
src/utils/jwt.test.ts
src/utils/server/jwt.ts
src/utils/server/jwt.test.ts
Bump dependencies for obfuscation support
  • Upgrade jose to ^6.0.12
  • Upgrade next to ^15.4.4
package.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@lobehubbot
Copy link
Member

👍 @arvinxx

Thank you for raising your pull request and contributing to our Community
Please make sure you have followed our contributing guidelines. We will review it as soon as possible.
If you encounter any problems, please feel free to connect with us.
非常感谢您提出拉取请求并为我们的社区做出贡献,请确保您已经遵循了我们的贡献指南,我们会尽快审查它。
如果您遇到任何问题,请随时与我们联系。

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jul 29, 2025
Copy link
Contributor

gru-agent bot commented Jul 29, 2025

TestGru Assignment

Summary

Link CommitId Status Reason
Detail fdb2e79 ✅ Finished

History Assignment

Files

File Pull Request
src/utils/server/xor.ts ❌ Failed (I failed to setup the environment.)
src/utils/client/xor-obfuscation.ts ❌ Failed (I failed to setup the environment.)
src/services/_auth.ts ❌ Failed (I failed to setup the environment.)

Tip

You can @gru-agent and leave your feedback. TestGru will make adjustments based on your input

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @arvinxx - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/utils/server/xor.ts:29` </location>
<code_context>
+  return new TextDecoder().decode(arr);
+};
+
+export const getXorPayload = (token: string): JWTPayload => {
+  const keyBytes = new TextEncoder().encode(SECRET_XOR_KEY);
+
</code_context>

<issue_to_address>
No validation or error handling for malformed or invalid tokens.

Add error handling for invalid base64 input or malformed JSON to prevent unhandled exceptions and ensure the function fails gracefully.
</issue_to_address>

### Comment 2
<location> `src/utils/client/xor-obfuscation.ts:38` </location>
<code_context>
+
+  // 将 Uint8Array 转换为 Base64 字符串
+  // 浏览器环境 btoa 只能处理 Latin-1 字符,所以需要先转换为适合 btoa 的字符串
+  return btoa(String.fromCharCode(...xoredBytes));
+};
</code_context>

<issue_to_address>
Potential performance issue with large payloads using spread operator.

For large Uint8Arrays, the spread operator may cause stack overflows or slowdowns. Use a loop or Buffer to convert the array before encoding.

Suggested implementation:

```typescript
  // 将 Uint8Array 转换为 Base64 字符串
  // 浏览器环境 btoa 只能处理 Latin-1 字符,所以需要先转换为适合 btoa 的字符串
  return btoa(uint8ArrayToString(xoredBytes));

```

```typescript

/**
 * 将 Uint8Array 转换为 Latin-1 字符串,适用于 btoa
 * 避免使用扩展运算符以防止堆栈溢出
 */
function uint8ArrayToString(arr: Uint8Array): string {
  let result = '';
  const chunkSize = 0x8000; // 32k chunks
  for (let i = 0; i < arr.length; i += chunkSize) {
    result += String.fromCharCode.apply(
      null,
      arr.subarray(i, Math.min(i + chunkSize, arr.length)) as unknown as number[]
    );
  }
  return result;
}

/**
 * 对 Uint8Array 进行 XOR 运算
 * @param data 要处理的 Uint8Array
 * @param key 用于 XOR 的密钥 (Uint8Array)
 * @returns 经过 XOR 运算的 Uint8Array
 */
const xorProcess = (data: Uint8Array, key: Uint8Array): Uint8Array => {
  const result = new Uint8Array(data.length);
  for (const [i, datum] of data.entries()) {

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

return new TextDecoder().decode(arr);
};

export const getXorPayload = (token: string): JWTPayload => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): No validation or error handling for malformed or invalid tokens.

Add error handling for invalid base64 input or malformed JSON to prevent unhandled exceptions and ensure the function fails gracefully.


// 将 Uint8Array 转换为 Base64 字符串
// 浏览器环境 btoa 只能处理 Latin-1 字符,所以需要先转换为适合 btoa 的字符串
return btoa(String.fromCharCode(...xoredBytes));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Potential performance issue with large payloads using spread operator.

For large Uint8Arrays, the spread operator may cause stack overflows or slowdowns. Use a loop or Buffer to convert the array before encoding.

Suggested implementation:

  // 将 Uint8Array 转换为 Base64 字符串
  // 浏览器环境 btoa 只能处理 Latin-1 字符,所以需要先转换为适合 btoa 的字符串
  return btoa(uint8ArrayToString(xoredBytes));
/**
 * 将 Uint8Array 转换为 Latin-1 字符串,适用于 btoa
 * 避免使用扩展运算符以防止堆栈溢出
 */
function uint8ArrayToString(arr: Uint8Array): string {
  let result = '';
  const chunkSize = 0x8000; // 32k chunks
  for (let i = 0; i < arr.length; i += chunkSize) {
    result += String.fromCharCode.apply(
      null,
      arr.subarray(i, Math.min(i + chunkSize, arr.length)) as unknown as number[]
    );
  }
  return result;
}

/**
 * 对 Uint8Array 进行 XOR 运算
 * @param data 要处理的 Uint8Array
 * @param key 用于 XOR 的密钥 (Uint8Array)
 * @returns 经过 XOR 运算的 Uint8Array
 */
const xorProcess = (data: Uint8Array, key: Uint8Array): Uint8Array => {
  const result = new Uint8Array(data.length);
  for (const [i, datum] of data.entries()) {

Copy link

codecov bot commented Jul 29, 2025

Codecov Report

❌ Patch coverage is 97.91667% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 85.63%. Comparing base (fdaa725) to head (736000e).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
src/libs/trpc/lambda/middleware/keyVaults.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8595      +/-   ##
==========================================
+ Coverage   85.58%   85.63%   +0.05%     
==========================================
  Files         910      910              
  Lines       69093    69346     +253     
  Branches     4530     6664    +2134     
==========================================
+ Hits        59134    59387     +253     
  Misses       9959     9959              
Flag Coverage Δ
app 85.63% <97.91%> (+0.05%) ⬆️
server 96.26% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vercel vercel bot temporarily deployed to Preview – lobe-chat-preview July 29, 2025 04:33 Inactive
@vercel vercel bot temporarily deployed to Preview – lobe-chat-database July 29, 2025 04:52 Inactive
@arvinxx arvinxx merged commit be98d56 into main Jul 29, 2025
22 of 24 checks passed
@arvinxx arvinxx deleted the refactor/jose-jwt-to-xor branch July 29, 2025 06:37
@lobehubbot
Copy link
Member

❤️ Great PR @arvinxx ❤️

The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our discord and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world.
项目的成长离不开用户反馈和贡献,感谢您的贡献! 如果您对 LobeHub 开发者社区感兴趣,请加入我们的 discord,然后私信 @arvinxx@canisminor1990。他们会邀请您加入我们的私密开发者频道。我们将会讨论关于 Lobe Chat 的开发,分享和讨论全球范围内的 AI 消息。

github-actions bot pushed a commit that referenced this pull request Jul 29, 2025
### [Version&nbsp;1.105.2](v1.105.1...v1.105.2)
<sup>Released on **2025-07-29**</sup>

#### ♻ Code Refactoring

- **misc**: Clean mcp sitemap, refactor jose-JWT to xor obfuscation.

#### 💄 Styles

- **misc**: Add more OpenAI SDK Text2Image providers, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Clean mcp sitemap, closes [#8596](#8596) ([b9e3e66](b9e3e66))
* **misc**: Refactor jose-JWT to xor obfuscation, closes [#8595](#8595) ([be98d56](be98d56))

#### Styles

* **misc**: Add more OpenAI SDK Text2Image providers, closes [#8573](#8573) ([403aebd](403aebd))
* **misc**: Update i18n, closes [#8593](#8593) ([356cf0c](356cf0c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
@lobehubbot
Copy link
Member

🎉 This PR is included in version 1.105.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

github-actions bot pushed a commit to jaworldwideorg/OneJA-Bot that referenced this pull request Jul 29, 2025
### [Version&nbsp;1.104.1](v1.104.0...v1.104.1)
<sup>Released on **2025-07-29**</sup>

#### ♻ Code Refactoring

- **misc**: Clean mcp sitemap, refactor jose-JWT to xor obfuscation.

#### 💄 Styles

- **misc**: Add more OpenAI SDK Text2Image providers, support more Text2Image from Qwen, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Clean mcp sitemap, closes [lobehub#8596](https://github.com/jaworldwideorg/OneJA-Bot/issues/8596) ([b9e3e66](b9e3e66))
* **misc**: Refactor jose-JWT to xor obfuscation, closes [lobehub#8595](https://github.com/jaworldwideorg/OneJA-Bot/issues/8595) ([be98d56](be98d56))

#### Styles

* **misc**: Add more OpenAI SDK Text2Image providers, closes [lobehub#8573](https://github.com/jaworldwideorg/OneJA-Bot/issues/8573) ([403aebd](403aebd))
* **misc**: Support more Text2Image from Qwen, closes [lobehub#8574](https://github.com/jaworldwideorg/OneJA-Bot/issues/8574) ([b8c0e2d](b8c0e2d))
* **misc**: Update i18n, closes [lobehub#8593](https://github.com/jaworldwideorg/OneJA-Bot/issues/8593) ([356cf0c](356cf0c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
bbbugg pushed a commit to bbbugg/lobe-chat that referenced this pull request Aug 14, 2025
* refactor jose-jwt to xor obfuscation

* rename JWTPayload type to ClientSecretPayload

* fix tests

* revert next version
bbbugg pushed a commit to bbbugg/lobe-chat that referenced this pull request Aug 14, 2025
### [Version&nbsp;1.105.2](lobehub/lobe-chat@v1.105.1...v1.105.2)
<sup>Released on **2025-07-29**</sup>

#### ♻ Code Refactoring

- **misc**: Clean mcp sitemap, refactor jose-JWT to xor obfuscation.

#### 💄 Styles

- **misc**: Add more OpenAI SDK Text2Image providers, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Clean mcp sitemap, closes [lobehub#8596](lobehub#8596) ([9715349](lobehub@9715349))
* **misc**: Refactor jose-JWT to xor obfuscation, closes [lobehub#8595](lobehub#8595) ([06b7164](lobehub@06b7164))

#### Styles

* **misc**: Add more OpenAI SDK Text2Image providers, closes [lobehub#8573](lobehub#8573) ([221488e](lobehub@221488e))
* **misc**: Update i18n, closes [lobehub#8593](lobehub#8593) ([89d4059](lobehub@89d4059))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
cy948 pushed a commit to cy948/lobe-chat that referenced this pull request Aug 23, 2025
* refactor jose-jwt to xor obfuscation

* rename JWTPayload type to ClientSecretPayload

* fix tests

* revert next version
cy948 pushed a commit to cy948/lobe-chat that referenced this pull request Aug 23, 2025
### [Version&nbsp;1.105.2](lobehub/lobe-chat@v1.105.1...v1.105.2)
<sup>Released on **2025-07-29**</sup>

#### ♻ Code Refactoring

- **misc**: Clean mcp sitemap, refactor jose-JWT to xor obfuscation.

#### 💄 Styles

- **misc**: Add more OpenAI SDK Text2Image providers, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Clean mcp sitemap, closes [lobehub#8596](lobehub#8596) ([b9e3e66](lobehub@b9e3e66))
* **misc**: Refactor jose-JWT to xor obfuscation, closes [lobehub#8595](lobehub#8595) ([be98d56](lobehub@be98d56))

#### Styles

* **misc**: Add more OpenAI SDK Text2Image providers, closes [lobehub#8573](lobehub#8573) ([403aebd](lobehub@403aebd))
* **misc**: Update i18n, closes [lobehub#8593](lobehub#8593) ([356cf0c](lobehub@356cf0c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
released size:XL This PR changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants