Skip to content

Commit c8f6923

Browse files
authored
Add OAuth2 middleware and enhance inbound auth docs (#474)
* Add OAuth2 middleware and enhance inbound auth docs Introduced veadk.auth.middleware.oauth2_auth.py, providing OAuth2 3LO middleware for Starlette/FastAPI with VeIdentity integration, including session management, PKCE, and extensible state storage. Updated inbound authentication documentation to detail both API Gateway and middleware OAuth2 SSO integration, with code samples, configuration options, and deployment guidance. Create __init__.py Refactor exception handling and formatting in oauth2_auth.py Simplified exception raising in _fetch_oidc_discovery by consolidating RuntimeError messages into single lines. Improved code readability by reformatting function calls to follow consistent indentation. * Set user pool sign-up and recovery options Explicitly disables self sign-up and enables self account recovery when creating a user pool in IdentityClient.
1 parent 9812b20 commit c8f6923

File tree

4 files changed

+1732
-50
lines changed

4 files changed

+1732
-50
lines changed

docs/docs/auth/inbound.md

Lines changed: 228 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,262 @@
11
---
2-
titie: 入站认证
2+
title: 入站认证
33
description: 通过入站认证访问 Agent
44
navigation:
55
icon: i-lucide-lock
66
---
77

8-
VeADK 支持 API key 和 OAuth2 方式的入站认证。
8+
VeADK 支持 API Key 和 OAuth2 方式的入站认证。
99

1010
## API Key 认证
1111

12-
API key 认证是通过唯一字符串密钥验证请求方身份、授权访问 API 资源的常见认证方式。VeADK 约定将 API key 通过 URL 的 `token` 参数传递。
12+
API Key 认证是通过唯一字符串密钥验证请求方身份、授权访问 API 资源的常见认证方式。VeADK 约定将 API Key 通过 URL 的 `token` 参数传递。
1313

1414
!!! tip
15-
API key 仅适用于 A2A/MCP Server 部署模式,不建议在 VeADK Web 部署模式中使用,更推荐采用 OAuth2 认证。
15+
API Key 仅适用于 A2A/MCP Server 部署模式,不建议在 VeADK Web 部署模式中使用,更推荐采用 OAuth2 认证。
1616

1717
### 使用方式
1818

19-
您可以通过脚手架创建 Agent 时指定 API key 认证方式,或者部署已有项目时添加 `--auth-method=api-key` 参数启用该认证。
19+
您可以通过脚手架创建 Agent 时指定 API Key 认证方式,或者部署已有项目时添加 `--auth-method=api-key` 参数启用该认证。
2020

21-
当用户访问应用时,API 网关将验证用户 `token` URL 参数中携带的 API key
21+
当用户访问应用时,API 网关将验证用户 `token` URL 参数中携带的 API Key
2222

2323
## OAuth2 单点登录
2424

2525
OAuth2 是一种开放标准的授权框架,通过令牌而非直接暴露账号密码,安全实现第三方应用对资源的有限访问。
2626

27-
OAuth2 单点登录是基于 OAuth2 授权框架实现的身份认证方案,用户一次登录后可免重复验证访问多个关联应用。VeADK Web 支持 OAuth2 单点登录的认证方式。
27+
OAuth2 单点登录是基于 OAuth2 授权框架实现的身份认证方案,用户一次登录后可免重复验证访问多个关联应用。VeADK 提供两种 OAuth2 单点登录的接入方式:
2828

29+
| 方式 | 适用场景 | 说明 |
30+
|------|----------|------|
31+
| API 网关模式 | VeFaaS 云端部署 | 通过脚手架部署,由 API 网关处理认证 |
32+
| Starlette/FastAPI 中间件 | 本地开发 / 自托管部署 | 在应用内集成 OAuth2 中间件 |
2933

30-
!!! tip
31-
使用 OAuth2 单点登录需要版本为 4.0.0 及以上的 API 网关。
34+
### 方式一:API 网关模式(VeFaaS 部署)
3235

36+
适用于通过 VeFaaS 部署的 VeADK Web 应用,由 API 网关处理 OAuth2 认证流程。
3337

34-
### 使用方式
38+
!!! tip
39+
使用 API 网关模式需要版本为 4.0.0 及以上的 API 网关。
40+
41+
#### 使用方式
3542

3643
您可以通过脚手架创建 Agent 时指定 OAuth2 认证方式,或者部署已有项目时添加 `--auth-method=oauth2` 参数启用该认证,VeADK 将自动为您创建 Identity 用户池和客户端。如果您需要使用已有的用户池或客户端,您可以在部署时添加 `--user-pool-name``--client-name` 参数指定用户池和客户端。
3744

38-
在部署 VeADK Web 应用后,您可以在 Identity 中创建用户
45+
在部署 VeADK Web 应用后,您可以在 Identity 中创建用户
3946

4047
1. 登录火山引擎控制台,导航到 Agent Identity 服务
4148
2. 在左侧导航树中,选择 身份认证 > 用户池管理,选择用户池
4249
3. 在用户池的用户标签页中,点击 新建用户,填写用户信息并点击 确定
4350

4451
当用户访问 VeADK Web 应用时,API 网关将引导用户至登录页完成登录。您可以在 `Authorization` 请求头中获得用户的 JWT 令牌。
4552

53+
### 方式二:Starlette/FastAPI 中间件(本地/自托管)
54+
55+
适用于本地开发或自托管部署场景,通过 VeADK 提供的中间件在应用内处理 OAuth2 认证。支持所有基于 Starlette 的框架,包括 FastAPI。
56+
57+
#### 快速开始(FastAPI)
58+
59+
推荐使用 `OAuth2Config.from_veidentity()` 方法,自动配置 VeIdentity User Pool:
60+
61+
```python
62+
from fastapi import FastAPI
63+
from veadk.auth.middleware.oauth2_auth import OAuth2Config, setup_oauth2
64+
65+
app = FastAPI()
66+
67+
setup_oauth2(
68+
app,
69+
OAuth2Config.from_veidentity(
70+
user_pool_name="my-app",
71+
client_name="my-app-web",
72+
redirect_uri="https://myapp.com/oauth2/callback",
73+
),
74+
)
75+
```
76+
77+
#### 快速开始(Starlette)
78+
79+
```python
80+
from starlette.applications import Starlette
81+
from veadk.auth.middleware.oauth2_auth import OAuth2Config, setup_oauth2
82+
83+
app = Starlette()
84+
85+
setup_oauth2(
86+
app,
87+
OAuth2Config.from_veidentity(
88+
user_pool_name="my-app",
89+
client_name="my-app-web",
90+
redirect_uri="https://myapp.com/oauth2/callback",
91+
),
92+
)
93+
```
94+
95+
该方法会自动:
96+
97+
- 创建用户池(如不存在)
98+
- 创建用户池客户端(如不存在)
99+
- 注册回调 URL
100+
- 配置 OAuth2 端点
101+
102+
#### 使用已有资源
103+
104+
如果您已有用户池和客户端,可以禁用自动创建:
105+
106+
```python
107+
setup_oauth2(
108+
app,
109+
OAuth2Config.from_veidentity(
110+
user_pool_name="existing-pool",
111+
client_name="existing-client",
112+
redirect_uri="https://myapp.com/oauth2/callback",
113+
auto_create=False, # 资源不存在时报错
114+
auto_register_callback=False, # 不修改回调 URL
115+
),
116+
)
117+
```
118+
119+
#### 本地开发配置
120+
121+
本地开发时需要禁用 HTTPS cookie:
122+
123+
```python
124+
setup_oauth2(
125+
app,
126+
OAuth2Config.from_veidentity(
127+
user_pool_name="my-app",
128+
client_name="my-app-web",
129+
redirect_uri="http://localhost:8000/oauth2/callback",
130+
cookie_secure=False, # 本地 HTTP 开发
131+
),
132+
)
133+
```
134+
135+
#### 自定义 OAuth2 Provider
136+
137+
如需接入非 VeIdentity 的 OAuth2 提供商,可直接配置 `OAuth2Config`
138+
139+
```python
140+
setup_oauth2(
141+
app,
142+
OAuth2Config(
143+
authorize_url="https://provider.com/oauth2/authorize",
144+
token_url="https://provider.com/oauth2/token",
145+
userinfo_url="https://provider.com/oauth2/userinfo",
146+
client_id="your-client-id",
147+
client_secret="your-client-secret",
148+
redirect_uri="https://myapp.com/oauth2/callback",
149+
),
150+
)
151+
```
152+
153+
#### 路由说明
154+
155+
中间件会自动注册以下路由:
156+
157+
| 路由 | 说明 |
158+
|------|------|
159+
| `/oauth2/login` | 发起 OAuth2 登录流程 |
160+
| `/oauth2/callback` | OAuth2 回调处理 |
161+
| `/oauth2/logout` | 登出并清除会话 |
162+
| `/oauth2/userinfo` | 获取当前用户信息 |
163+
164+
#### 免认证路径
165+
166+
可以配置跳过认证的路径:
167+
168+
```python
169+
setup_oauth2(
170+
app,
171+
config,
172+
exempt_paths=["/health", "/metrics"], # 精确匹配
173+
exempt_prefixes=["/public/", "/static/"], # 前缀匹配
174+
)
175+
```
176+
177+
#### API 请求处理
178+
179+
中间件会根据请求类型自动选择响应方式:
180+
181+
- **浏览器请求**:重定向到登录页面
182+
- **API 请求**:返回 `401 Unauthorized` JSON 响应
183+
184+
API 请求通过以下方式识别:
185+
186+
- `Accept: application/json` 请求头
187+
- 路径前缀匹配(默认 `/api/`
188+
- `X-Requested-With: XMLHttpRequest` 请求头
189+
190+
可通过 `api_path_prefixes` 参数自定义:
191+
192+
```python
193+
OAuth2Config.from_veidentity(
194+
# ...
195+
api_path_prefixes=["/api/", "/graphql"],
196+
)
197+
```
198+
199+
#### 配置参考
200+
201+
##### OAuth2Config.from_veidentity() 参数
202+
203+
| 参数 | 默认值 | 说明 |
204+
|------|--------|------|
205+
| `user_pool_name` | (必填) | VeIdentity 用户池名称 |
206+
| `client_name` | (必填) | 用户池客户端名称 |
207+
| `redirect_uri` | (必填) | OAuth2 回调 URL |
208+
| `auto_create` | `True` | 资源不存在时自动创建 |
209+
| `auto_register_callback` | `True` | 自动注册回调 URL |
210+
| `client_type` | `WEB_APPLICATION` | 客户端类型 |
211+
| `scope` | `"openid profile email"` | OAuth2 作用域 |
212+
| `**extra_config` | - | 其他 OAuth2Config 参数 |
213+
214+
##### OAuth2Config 参数
215+
216+
| 参数 | 默认值 | 说明 |
217+
|------|--------|------|
218+
| `session_timeout_seconds` | `3600` | 会话超时时间(秒) |
219+
| `cookie_secure` | `True` | 是否启用安全 cookie |
220+
| `auto_refresh_token` | `True` | 自动刷新令牌 |
221+
| `token_refresh_threshold_seconds` | `300` | 令牌刷新阈值(秒) |
222+
| `api_path_prefixes` | `["/api/"]` | API 路径前缀 |
223+
224+
#### 分布式部署
225+
226+
默认的 `InMemoryStateStore` 仅适用于单进程部署。分布式场景需要使用 Redis 等外部存储:
227+
228+
```python
229+
class RedisStateStore:
230+
def __init__(self, redis_client, ttl: int = 300):
231+
self._redis = redis_client
232+
self._ttl = ttl
233+
234+
def create_state(self, redirect_after_auth: str = "/", code_verifier=None) -> str:
235+
import secrets, json
236+
state = secrets.token_urlsafe(32)
237+
self._redis.setex(
238+
f"oauth2:{state}",
239+
self._ttl,
240+
json.dumps({
241+
"redirect_after_auth": redirect_after_auth,
242+
"code_verifier": code_verifier,
243+
}),
244+
)
245+
return state
246+
247+
def validate_and_consume_state(self, state: str):
248+
import json
249+
key = f"oauth2:{state}"
250+
data = self._redis.get(key)
251+
if not data:
252+
return None
253+
self._redis.delete(key)
254+
return json.loads(data)
255+
256+
# 使用
257+
setup_oauth2(app, config, state_store=RedisStateStore(redis_client))
258+
```
259+
46260
## OAuth2 JWT 认证
47261

48262
OAuth2 JWT 认证是将 OAuth2 授权框架与 JWT 结合,用 JWT 格式承载授权令牌的认证方式。A2A/MCP Server 支持 OAuth2 JWT 的认证方式。
@@ -51,13 +265,13 @@ OAuth2 JWT 认证是将 OAuth2 授权框架与 JWT 结合,用 JWT 格式承载
51265

52266
您可以通过脚手架创建 Agent 时指定 OAuth2 认证方式,或者部署已有项目时添加 `--auth-method=oauth2` 参数启用该认证,VeADK 将自动为您创建 Identity 用户池。如果您需要使用已有的用户池,您可以在部署时添加 `--user-pool-name` 参数指定用户池。
53267

54-
在部署 A2A/MCP Server 应用后,您可以在 Identity 中管理客户端
268+
在部署 A2A/MCP Server 应用后,您可以在 Identity 中管理客户端
55269

56270
1. 登录火山引擎控制台,导航到 Agent Identity 服务
57271
2. 在左侧导航树中,选择 身份认证 > 用户池管理,选择用户池
58272
3. 在客户端的用户标签中,点击 新建客户端,填写 客户端名称,选择 客户端类型 并点击确定
59273

60-
您可以创建 M2M 类型的客户端用于验证。您可以使用以下 curl 命令生成 JWT 令牌
274+
您可以创建 M2M 类型的客户端用于验证。您可以使用以下 curl 命令生成 JWT 令牌
61275

62276
```bash
63277
REGION="cn-beijing"
@@ -71,4 +285,4 @@ curl --location "https://userpool-${USER_POOL_ID}.userpool.auth.id.${REGION}.vol
71285
--data-urlencode "grant_type=client_credentials"
72286
```
73287

74-
当用户访问 A2A/MCP Server 应用时,API 网关将验证用户携带的 JWT 令牌。您可以在 `Authorization` 请求头中获得用户的 JWT 令牌。
288+
当用户访问 A2A/MCP Server 应用时,API 网关将验证用户携带的 JWT 令牌。您可以在 `Authorization` 请求头中获得用户的 JWT 令牌。

veadk/auth/middleware/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.

0 commit comments

Comments
 (0)