Skip to content

Commit dd5c8b8

Browse files
niiish32x越鸿csunnyanotherso1adusx1981
authored
feat: oauth2 login and simple user manage (#164)
Co-authored-by: 越鸿 <nishenghao.nsh@oceanbase.com> Co-authored-by: magic.chen <cfqsunny@163.com> Co-authored-by: AnotherSola <38176179+anotherso1a@users.noreply.github.com> Co-authored-by: 坐山客 <157097695@qq.com> Co-authored-by: Aries-ckt <916701291@qq.com> Co-authored-by: heyzcat <31226585+heyzcat@users.noreply.github.com> Co-authored-by: yangchuan <yangchuan@oppo.com> Co-authored-by: neuqliu <1196932066@qq.com> Co-authored-by: yanzhiyong <932374019@qq.com> Co-authored-by: gallopxiong <62653374+josehap@users.noreply.github.com> Co-authored-by: gallopxiong <gallopxiong@tencent.com> Co-authored-by: XinyueDu <51403464+XinyueDu@users.noreply.github.com> Co-authored-by: duxinyue.dxy <duxinyue.dxy@antgroup.com> Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com> Co-authored-by: lpq131004 <66124950+lpq131004@users.noreply.github.com> Co-authored-by: chenketing.ckt <chenketing.ckt@antgroup.com> Co-authored-by: Aries-ckt <ariesketing@gmail.com> Co-authored-by: Lin-Zhipeng <2542207527@qq.com> Co-authored-by: zhipeng.lin <zhipeng.lin@shopee.com> Co-authored-by: Claude (GLM-4.7) <noreply@anthropic.com> Co-authored-by: tptpp <544016459@qq.com> Co-authored-by: RichardoMu <44485717+RichardoMrMu@users.noreply.github.com> Co-authored-by: RichardoMrMu <tianbowen.tbw@antgroup.com> Co-authored-by: yhjun1026 <yhjun1026@users.noreply.github.com>
1 parent 1381ccb commit dd5c8b8

184 files changed

Lines changed: 3334 additions & 1648 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
name: OAuth2 Login Plugin
3+
overview: 在 设置->系统配置 中增加 OAuth2 插件配置,支持 GitHub 及自定义 OAuth2 服务器。默认关闭,开启后实现基于 OAuth 2.0 协议的用户登录鉴权,与现有逻辑完全向前兼容。
4+
todos:
5+
- id: schema-oauth2
6+
content: 在 derisk_core config schema 中新增 OAuth2ProviderConfig、OAuth2Config,扩展 AppConfig
7+
status: completed
8+
- id: user-ddl
9+
content: 扩展 user 表 DDL(oauth_provider、oauth_id、email、avatar)
10+
status: completed
11+
- id: auth-module
12+
content: 新建 derisk_app/auth 模块(OAuth 流程、session 管理、用户服务)
13+
status: completed
14+
- id: auth-api
15+
content: 新建 auth_api.py,实现 login/callback/me/logout 路由,并注册到 api_v1
16+
status: completed
17+
- id: config-api-oauth2
18+
content: 扩展 config_api 支持 oauth2 配置读写
19+
status: completed
20+
- id: settings-oauth2-card
21+
content: 在 settings/config 页 VisualConfig 中新增 OAuth2 插件配置 Card
22+
status: completed
23+
- id: login-page
24+
content: 新建 /login 页面,展示 OAuth 登录按钮
25+
status: completed
26+
- id: auth-callback-page
27+
content: 新建 /auth/callback 页面处理 OAuth 回调
28+
status: completed
29+
- id: auth-service
30+
content: 新建 web services/auth.ts,封装 auth/me、oauth/status 等 API 调用
31+
status: completed
32+
- id: layout-auth
33+
content: 修改 layout.tsx 鉴权逻辑,OAuth 开启时校验登录状态并支持重定向
34+
status: completed
35+
isProject: false
36+
---
37+
38+
# OAuth2 登录插件配置方案
39+
40+
## 一、架构设计原则
41+
42+
- **协议优先**:采用标准 OAuth 2.0 / OpenID Connect 协议,不绑定任何特定系统
43+
- **可插拔**:OAuth2 为可选开关,默认关闭时行为与当前完全一致
44+
- **用户模块**:建立独立的用户与权限管理模块,登录可接入外部 OAuth2 协议
45+
- **第一版范围**:仅实现用户登录鉴权,不与 LLM key、tool 鉴权关联
46+
47+
## 二、当前状态分析
48+
49+
50+
| 模块 | 现状 |
51+
| ------ | ---------------------------------------------------------------------------------------------------------- |
52+
| 配置存储 | [derisk_core/config](packages/derisk-core/src/derisk_core/config/schema.py) 的 AppConfig,持久化到 `derisk.json` |
53+
| 配置 API | [config_api.py](packages/derisk-app/src/derisk_app/openapi/api_v1/config_api.py) 提供 `/api/v1/config/`* |
54+
| 设置页面 | [settings/config/page.tsx](web/src/app/settings/config/page.tsx) 含模型、Agent、沙箱、授权、工具等 Tab |
55+
| 登录逻辑 | [layout.tsx](web/src/app/layout.tsx)`handleAuth` 为 MOCK,直接设置假用户到 localStorage |
56+
| 用户表 | `user` 表存在(id, name, fullname),可扩展用于 OAuth 用户 |
57+
58+
59+
## 三、配置结构设计
60+
61+
在 AppConfig 中新增 `oauth2` 字段(默认关闭):
62+
63+
```python
64+
# OAuth2 配置结构
65+
oauth2:
66+
enabled: false # 开关,默认 false
67+
providers:
68+
- id: "github" # 预设:github
69+
type: "github" # github | custom
70+
client_id: ""
71+
client_secret: ""
72+
# custom 类型额外需要:
73+
# authorization_url, token_url, userinfo_url, scope
74+
```
75+
76+
- **GitHub 预设**:固定使用 `https://github.com/login/oauth/authorize` 等标准端点
77+
- **自定义**:支持任意 OAuth2 兼容服务器(authorization_url、token_url、userinfo_url)
78+
79+
## 四、实现模块
80+
81+
### 4.1 后端
82+
83+
**1. 配置 Schema 扩展**
84+
85+
- 文件:[packages/derisk-core/src/derisk_core/config/schema.py](packages/derisk-core/src/derisk_core/config/schema.py)
86+
- 新增 `OAuth2ProviderConfig``OAuth2Config`,在 `AppConfig` 中增加 `oauth2: Optional[OAuth2Config] = None`
87+
88+
**2. OAuth2 认证 API**
89+
90+
- 新建 `packages/derisk-app/src/derisk_app/openapi/api_v1/auth_api.py`
91+
- 路由:
92+
- `GET /api/v1/auth/oauth/login?provider=github`:生成 state,重定向到 OAuth 授权页
93+
- `GET /api/v1/auth/oauth/callback`:处理回调,用 code 换 token,拉取用户信息
94+
- `GET /api/v1/auth/me`:返回当前登录用户(校验 session/token)
95+
- `POST /api/v1/auth/logout`:登出
96+
97+
**3. 用户模块**
98+
99+
- 扩展 `user` 表:`oauth_provider``oauth_id``email``avatar`(通过 DDL 升级)
100+
- 首次 OAuth 登录时创建/更新本地用户
101+
- 新建 `packages/derisk-app/src/derisk_app/auth/`:OAuth 流程、session 管理、用户服务
102+
103+
**4. 配置 API 扩展**
104+
105+
-[config_api.py](packages/derisk-app/src/derisk_app/openapi/api_v1/config_api.py) 中支持读写 `oauth2` 配置
106+
- 或通过现有 `import_config``extra` 机制透传(需保证 schema 校验)
107+
108+
### 4.2 前端
109+
110+
**1. 设置页 OAuth2 配置卡片**
111+
112+
- 文件:[web/src/app/settings/config/page.tsx](web/src/app/settings/config/page.tsx)
113+
-`VisualConfig` 中新增 Card「OAuth2 登录配置」
114+
- 内容:Switch(enabled)、Provider 列表(GitHub + 自定义)、表单(client_id、client_secret 等)
115+
116+
**2. 登录页**
117+
118+
- 新建 `web/src/app/login/page.tsx`
119+
- 根据配置展示「使用 GitHub 登录」「使用 XXX 登录」等按钮
120+
- 点击后跳转到 `/api/v1/auth/oauth/login?provider=xxx`
121+
122+
**3. 回调页**
123+
124+
- 新建 `web/src/app/auth/callback/page.tsx`
125+
- 接收后端重定向,从 URL 获取 token/session 信息并写入 localStorage,再跳转到首页
126+
127+
**4. Layout 鉴权逻辑**
128+
129+
- 文件:[web/src/app/layout.tsx](web/src/app/layout.tsx)
130+
- 调用 `GET /api/v1/auth/oauth/status``GET /api/v1/auth/me` 判断是否启用 OAuth 及是否已登录
131+
- 当 OAuth 关闭:保持现有 MOCK 逻辑(或直接视为已登录)
132+
- 当 OAuth 开启且未登录:重定向到 `/login`
133+
134+
## 五、数据流示意
135+
136+
```mermaid
137+
flowchart TB
138+
subgraph Config [配置层]
139+
A[settings/config]
140+
A --> B[oauth2.enabled=false]
141+
A --> C[oauth2.providers]
142+
end
143+
144+
subgraph AuthFlow [OAuth 开启时]
145+
D[用户访问] --> E{已登录?}
146+
E -->|否| F[重定向 /login]
147+
F --> G[点击 GitHub 登录]
148+
G --> H[/auth/oauth/login]
149+
H --> I[OAuth 授权页]
150+
I --> J[/auth/oauth/callback]
151+
J --> K[创建/更新用户]
152+
K --> L[写入 Session]
153+
L --> M[重定向首页]
154+
E -->|是| N[正常访问]
155+
end
156+
157+
subgraph Compat [OAuth 关闭时]
158+
O[用户访问] --> P[沿用现有逻辑]
159+
P --> Q[无登录校验]
160+
end
161+
162+
B -->|false| Compat
163+
B -->|true| AuthFlow
164+
```
165+
166+
167+
168+
## 六、关键文件清单
169+
170+
171+
| 操作 | 文件路径 |
172+
| --- | ------------------------------------------------------------------------- |
173+
| 修改 | `packages/derisk-core/src/derisk_core/config/schema.py` |
174+
| 新建 | `packages/derisk-app/src/derisk_app/auth/`(oauth 流程、用户服务) |
175+
| 新建 | `packages/derisk-app/src/derisk_app/openapi/api_v1/auth_api.py` |
176+
| 修改 | `packages/derisk-app/src/derisk_app/openapi/api_v1/api_v1.py`(注册 auth 路由) |
177+
| 修改 | `web/src/app/settings/config/page.tsx` |
178+
| 新建 | `web/src/app/login/page.tsx` |
179+
| 新建 | `web/src/app/auth/callback/page.tsx` |
180+
| 修改 | `web/src/app/layout.tsx` |
181+
| 新建 | `web/src/services/auth.ts` |
182+
| 修改 | `assets/schema/`(user 表 DDL 升级) |
183+
184+
185+
## 七、向后兼容与安全
186+
187+
- **默认关闭**`oauth2.enabled` 默认为 `false`,不改变现有行为
188+
- **Session 安全**:使用 HttpOnly Cookie 或 JWT,避免 token 暴露在前端
189+
- **State 防 CSRF**:OAuth 流程中校验 state 参数
190+
- **Secret 存储**:client_secret 仅存于服务端配置,不暴露给前端
191+
192+
## 八、后续扩展(本版不做)
193+
194+
- 与 LLM key、tool 鉴权的关联
195+
- 权限/角色管理(RBAC)
196+
- 多 OAuth 提供商同时启用时的策略选择
197+

assets/schema/derisk.sql

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use derisk;
99
-- MySQL DDL Script for Derisk
1010
-- Version: 0.3.0
1111
-- Generated from SQLAlchemy ORM Models
12-
-- Generated: 2026-03-15 19:47:08
12+
-- Generated: 2026-03-13 14:14:55
1313
-- ============================================================
1414

1515
SET NAMES utf8mb4;
@@ -37,27 +37,6 @@ CREATE TABLE IF NOT EXISTS `derisk_cluster_registry_instance` (
3737
UNIQUE KEY `uk_model_instance` (`model_name`, `host`, `port`, `sys_code`)
3838
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
3939

40-
-- Table: streaming_tool_config
41-
-- Source Model: StreamingToolConfig
42-
CREATE TABLE IF NOT EXISTS `streaming_tool_config` (
43-
`id` BIGINT NOT NULL AUTO_INCREMENT,
44-
`app_code` VARCHAR(128) NOT NULL COMMENT '应用代码',
45-
`tool_name` VARCHAR(128) NOT NULL COMMENT '工具名称',
46-
`tool_display_name` VARCHAR(256) NULL COMMENT '工具显示名称',
47-
`tool_description` TEXT NULL COMMENT '工具描述',
48-
`param_configs` JSON NOT NULL COMMENT '参数配置',
49-
`global_threshold` INT NULL DEFAULT 256 COMMENT '全局阈值',
50-
`global_strategy` VARCHAR(32) NULL COMMENT '全局策略',
51-
`global_renderer` VARCHAR(32) NULL COMMENT '全局渲染器',
52-
`enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用流式',
53-
`priority` INT NOT NULL DEFAULT 0 COMMENT '优先级',
54-
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
55-
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
56-
`created_by` VARCHAR(128) NULL COMMENT '创建人',
57-
`updated_by` VARCHAR(128) NULL COMMENT '更新人',
58-
PRIMARY KEY (`id`)
59-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
60-
6140
-- Table: chat_history
6241
-- Source Model: ChatHistoryEntity
6342
CREATE TABLE IF NOT EXISTS `chat_history` (
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- ============================================================
2+
-- OAuth2 User Table Extension
3+
-- Adds oauth_provider, oauth_id, email, avatar columns to user table
4+
-- ============================================================
5+
6+
SET NAMES utf8mb4;
7+
SET FOREIGN_KEY_CHECKS = 0;
8+
9+
-- Table: user - Add OAuth2 columns
10+
-- Run this once. If columns already exist, ALTER will fail (safe to ignore).
11+
ALTER TABLE `user` ADD COLUMN `oauth_provider` VARCHAR(64) NULL COMMENT 'OAuth2 provider (e.g. github, custom)';
12+
ALTER TABLE `user` ADD COLUMN `oauth_id` VARCHAR(255) NULL COMMENT 'User ID from OAuth provider';
13+
ALTER TABLE `user` ADD COLUMN `email` VARCHAR(255) NULL COMMENT 'User email';
14+
ALTER TABLE `user` ADD COLUMN `avatar` VARCHAR(512) NULL COMMENT 'Avatar URL';
15+
ALTER TABLE `user` ADD UNIQUE KEY `uk_oauth_provider_id` (`oauth_provider`, `oauth_id`);
16+
17+
SET FOREIGN_KEY_CHECKS = 1;

docs/docs/OAUTH2_CONFIG.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# OAuth2 登录配置说明
2+
3+
本文档说明如何在 OpenDerisk 中配置 OAuth2 登录。
4+
5+
## 配置入口
6+
7+
进入 **设置 → 系统配置**,点击 **「OAuth2 登录」** 标签页。
8+
9+
## 基本流程
10+
11+
1. 打开「启用 OAuth2 登录」开关
12+
2. 添加至少一个 OAuth2 提供商(GitHub 或自定义)
13+
3. 填写 Client ID、Client Secret 等必填项
14+
4. 点击「保存 OAuth2 配置」
15+
16+
关闭 OAuth2 时,系统使用原有逻辑,无需登录即可访问。
17+
18+
---
19+
20+
## GitHub 配置
21+
22+
### 1. 创建 GitHub OAuth App
23+
24+
1. 打开 [GitHub OAuth Apps 页面](https://github.com/settings/developers) → 点击 **OAuth Apps****New OAuth App**
25+
- 或直接访问:<https://github.com/settings/applications/new>
26+
2. 填写:
27+
- **Application name**:任意名称(如 OpenDerisk)
28+
- **Homepage URL**:应用访问地址,如 `http://localhost:3000`
29+
- **Authorization callback URL**`{应用地址}/api/v1/auth/oauth/callback`
30+
- 示例:`http://localhost:3000/api/v1/auth/oauth/callback`(本地开发)
31+
- 示例:`https://your-domain.com/api/v1/auth/oauth/callback`(生产环境)
32+
33+
### 2. 在 OpenDerisk 中填写
34+
35+
选择 **提供商类型** 为「GitHub」后,表单**仅显示** Client ID、Client Secret,无需填写任何 URL:
36+
37+
| 字段 | 说明 |
38+
|------|------|
39+
| 提供商类型 | 选择 `GitHub(仅需 Client ID / Secret)` |
40+
| Client ID | GitHub OAuth App 的 Client ID |
41+
| Client Secret | GitHub OAuth App 的 Client Secret |
42+
43+
### GitHub 完整示例(本地开发)
44+
45+
假设 OpenDerisk 运行在 `http://localhost:3000`,按以下步骤操作:
46+
47+
**步骤 1:在 GitHub 创建 OAuth App**
48+
49+
1. 打开 [GitHub Developer Settings](https://github.com/settings/developers) 或直接访问 [New OAuth App](https://github.com/settings/applications/new)
50+
2. 若从 Developer Settings 进入,点击 **OAuth Apps****New OAuth App**
51+
3. 填写表单:
52+
53+
| 字段 | 填写值 |
54+
|------|--------|
55+
| Application name | `OpenDerisk Local` |
56+
| Homepage URL | `http://localhost:3000` |
57+
| Authorization callback URL | `http://localhost:3000/api/v1/auth/oauth/callback` |
58+
59+
4. 点击 **Register application**
60+
5. 在应用详情页复制 **Client ID**,点击 **Generate a new client secret** 生成并复制 **Client secret**
61+
62+
**步骤 2:在 OpenDerisk 中配置**
63+
64+
1. 进入 **设置 → 系统配置 → OAuth2 登录**
65+
2. 打开「启用 OAuth2 登录」
66+
3. 填写提供商信息:
67+
68+
| 字段 | 填写值 |
69+
|------|--------|
70+
| 提供商类型 | `GitHub(仅需 Client ID / Secret)` |
71+
| Client ID | 粘贴 GitHub 的 Client ID(如 `Ov23liAbCdEf123456`|
72+
| Client Secret | 粘贴 GitHub 的 Client secret |
73+
74+
4. 点击 **保存 OAuth2 配置**
75+
76+
**步骤 3:验证**
77+
78+
1. 退出或刷新页面,应跳转到登录页
79+
2. 点击「使用 GitHub 登录」,完成授权后跳回应用
80+
81+
---
82+
83+
## 自定义 OAuth2 配置
84+
85+
选择 **提供商类型** 为「自定义 OAuth2」时,表单会**额外显示**以下 URL 字段,需全部填写:
86+
87+
| 字段 | 说明 | 示例 |
88+
|------|------|------|
89+
| Authorization URL | 授权端点 | `https://example.com/oauth/authorize` |
90+
| Token URL | 令牌端点 | `https://example.com/oauth/token` |
91+
| Userinfo URL | 用户信息端点 | `https://example.com/oauth/userinfo` |
92+
| Scope | 请求的权限范围 | `openid profile email` |
93+
94+
自定义 OAuth2 需符合标准 OAuth 2.0 流程,且 Userinfo 端点应返回包含 `id``sub` 的 JSON。
95+
96+
---
97+
98+
## 多提供商
99+
100+
可添加多个提供商(如 GitHub + 企业自建 OAuth2),用户登录时可选择任一提供商。点击「添加提供商」即可新增。
101+
102+
---
103+
104+
## 注意事项
105+
106+
- **Client Secret** 请妥善保管,不要泄露
107+
- 回调 URL 必须与 OAuth 应用配置中的完全一致,格式为:`{应用地址}/api/v1/auth/oauth/callback`
108+
- 本地开发时,Homepage URL 和 Callback URL 可使用 `http://localhost:端口`
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""OAuth2 authentication module."""
2+
3+
from .oauth import OAuth2Service
4+
from .session import SessionManager
5+
from .user_service import UserService
6+
7+
__all__ = ["OAuth2Service", "SessionManager", "UserService"]

0 commit comments

Comments
 (0)