OAuth2 API 接口文档
完整 API 规范: 手工维护的 OpenAPI 源文件为
apps/server/openapi.yaml(唯一契约源,CI 通过openapi-spec-validator+ 治理门校验三层一致性与版本同步);Swagger UI(/docs/api)在线浏览的是运行时由 Controller 代码生成的apps/server/docs/api/openapi.json(派生产物)。
本服务提供基于 OAuth2.0 标准(RFC 6749)的认证授权服务。
端点分类概览 (Endpoint Categories)
| 分类 | 描述 | 前缀 |
|---|---|---|
| Password Reset | 密码重置请求与确认(基于邮件验证码) | /api/password-reset |
| Email Verification | 邮箱验证发送与确认 | /api/email/verify |
| MFA (Multi-Factor Auth) | TOTP 设置、验证、恢复码管理 | /api/me/mfa(登录补全为 /oauth2/mfa/verify) |
| Admin API | 用户管理、客户端管理、审计日志(需 admin 角色) | /api/admin |
| User Self-Service | 用户个人资料更新、密码修改、会话管理 | /api/user |
| OIDC Discovery | OpenID Connect 发现端点与 JWKS | /.well-known/openid-configuration, /oauth2/jwks |
1. 授权端点 (Authorization Endpoint)
用于请求用户授权,获取 Authorization Code。
- URL:
/oauth2/authorize - Method:
GET - Access: 公开 (需登录)
请求参数 (Query Parameters)
| 参数名 | 必选 | 描述 | 示例 |
|---|---|---|---|
response_type | 是 | 必须为 code | code |
client_id | 是 | 客户端 ID | vue-client |
redirect_uri | 是 | 回调地址 (需完全匹配) | http://localhost:5173/callback |
scope | 否 | 申请的权限范围 | openid profile |
state | 建议 | 防止 CSRF 的随机串 | xyz123 |
code_challenge | 否 | PKCE code challenge(PUBLIC 客户端默认强制) | dBjftJeZ4CVK... |
code_challenge_method | 否 | plain 或 S256(提供 challenge 时默认 plain) | S256 |
nonce | 否 | OIDC nonce(防重放),openid scope 时回显到 id_token | n-0S6_WzA2Mj |
prompt | 否 | OIDC 提示值,空格分隔:none/login/consent/select_account(§3.1.2.1)。none 禁止 UI;login 强制重认证;consent 强制同意页。none 与其他值并用 → 400 | none |
max_age | 否 | 认证最大允許年齡(秒)。session auth_time 超齡 → 強制重认证 | 3600 |
响应
成功响应:
重定向至 redirect_uri,并附带 code 和 state。
HTTP/1.1 302 Found
Location: http://localhost:5173/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz123
错误响应: 直接返回 JSON 错误或重定向带 error 参数。
{
"error": "invalid_client",
"error_description": "Unknown client_id"
}
2. 令牌端点 (Token Endpoint)
用于使用 Authorization Code 换取 Access Token。
- URL:
/oauth2/token - Method:
POST - Access: 公开 (需 Client 认证)
- Content-Type:
application/x-www-form-urlencoded
请求参数 (Form Data)
| 参数名 | 必选 | 描述 | 示例 |
|---|---|---|---|
grant_type | 是 | 必须为 authorization_code | authorization_code |
code | 是 | 上一步获取的 code | SplxlOBeZQQYbYS6WxSbIA |
redirect_uri | 是 | 必须与获取 code 时一致 | http://localhost:5173/callback |
client_id | 是 | 客户端 ID | vue-client |
client_secret | 是 | 客户端密钥 (用于验证) | vue-secret |
响应
成功 (200 OK):
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"scope": "openid profile"
}
响应头(F-019,RFC 6749 §5.1 / RFC 7009 §2.2.1):所有 token / introspect /
revoke 成功响应都带 Cache-Control: no-store 与 Pragma: no-cache,禁止中间
代理缓存含憑证的响应体。
(注:grant_type=refresh_token 需先通过客户端认证(F-003/F-017,RFC 6749 §3.2.1/§6):认证方式必须匹配 客户端注册的 token_endpoint_auth_method——client_secret_basic(默认)仅接受 HTTP Basic(body 携带 client_secret 会被拒);client_secret_post 仅接受表单字段;PUBLIC 客户端仅发 client_id(携带 secret 会被拒)。缺失或错误返回 401 invalid_client。refresh token 持久化仅 Postgres 后端支持;storage_type="redis" 已弃用,该模式下 refresh grant 返回 unsupported_grant_type(F-005)。)
失败 (400/401):
{
"error": "invalid_grant",
"error_description": "Authorization code has expired"
}
失败 (429 Too Many Requests) — F-018 限流:/oauth2/token、
/oauth2/introspect、/oauth2/revoke 与 device_code 轮询共享一個进程内滑动窗口
限流器,按 (client_ip, client_id) 分桶。窗口内(默认 60s)失败計数達閾值
(默认 30,可經 custom_config["auth"]["rate_limit"] 配置 max_failures /
window_seconds)后,后续請求返回 429。仅計失败(认证/校验失败),成功清零。
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json
{
"error": "invalid_request",
"error_description": "Too many failed attempts; please retry later"
}