You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.3 KiB
6.3 KiB
OAuth2 API 接口文档
创建日期:2026-05-26
概述
OAuth2 模块提供标准化的第三方登录认证功能,支持多种 OAuth Provider,采用配置驱动的架构设计。
Base URL: /api/auth/oauth
接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /:provider/authorize |
生成授权 URL,重定向到 Provider |
| GET | /:provider/callback |
Provider 回调,处理 token 交换 |
| POST | /bind |
已登录用户绑定 OAuth 账号 |
| DELETE | /:provider/unbind |
解绑 OAuth 账号 |
| GET | /bindings |
获取当前用户的绑定列表 |
| GET | /:provider/status |
查询某 Provider 的绑定状态 |
1. 授权接口
生成授权 URL
GET /api/auth/oauth/:provider/authorize
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| provider | string | 是 | Provider 名称,如 github、google |
查询参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| bind | number | 否 | 绑定模式,传递 1 表示已登录用户绑定操作 |
| redirect_uri | string | 否 | 自定义回调跳转地址 |
响应: 302 Found
Location: https://github.com/login/oauth/authorize?client_id=xxx&redirect_uri=xxx&scope=xxx&state=xxx
错误响应:
| 状态码 | 错误码 | 说明 |
|---|---|---|
| 400 | OAUTH_PROVIDER_NOT_FOUND |
不支持的 Provider |
2. 回调接口
处理 Provider 回调
GET /api/auth/oauth/:provider/callback
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| provider | string | 是 | Provider 名称 |
查询参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| code | string | 是 | Provider 授权码 |
| state | string | 是 | 防止 CSRF 的状态令牌 |
响应: 302 Found
登录成功:
Location: /auth/login?oauth_success=1
绑定成功:
Location: /profile?bind_success=1
错误响应:
| 状态码 | 错误码 | 说明 |
|---|---|---|
| 400 | OAUTH_STATE_INVALID |
state 验证失败 |
| 400 | OAUTH_STATE_EXPIRED |
state 已过期 |
| 400 | OAUTH_TOKEN_EXCHANGE_FAILED |
token 交换失败 |
| 400 | OAUTH_USER_INFO_FAILED |
获取用户信息失败 |
| 400 | OAUTH_ALREADY_BIND |
账号已绑定其他用户 |
| 400 | OAUTH_BINDING_USER_MISMATCH |
绑定用户不匹配 |
错误跳转示例:
Location: /auth/login?oauth_error=OAUTH_STATE_EXPIRED
3. 绑定接口
绑定 OAuth 账号
POST /api/auth/oauth/bind
请求体:
{
"bindingToken": "string"
}
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| bindingToken | string | 是 | 回调时返回的临时 binding token |
响应:
{
"success": true,
"binding": {
"provider": "github",
"username": "username"
}
}
错误响应:
| 状态码 | 错误码 | 说明 |
|---|---|---|
| 400 | OAUTH_BINDING_TOKEN_INVALID |
binding token 无效或已过期 |
| 401 | UNAUTHORIZED |
未登录 |
4. 解绑接口
解绑 OAuth 账号
DELETE /api/auth/oauth/:provider/unbind
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| provider | string | 是 | Provider 名称 |
响应:
{
"success": true
}
错误响应:
| 状态码 | 错误码 | 说明 |
|---|---|---|
| 400 | OAUTH_LAST_BIND |
至少保留一个绑定方式(不能解绑最后一个) |
| 401 | UNAUTHORIZED |
未登录 |
5. 绑定列表接口
获取绑定列表
GET /api/auth/oauth/bindings
响应:
{
"bindings": [
{
"id": 1,
"provider": "github",
"username": "username",
"email": "user@example.com",
"avatar": "https://avatars.githubusercontent.com/u/xxx",
"boundAt": "2026-05-26T10:00:00.000Z"
}
]
}
6. 绑定状态接口
查询单个 Provider 绑定状态
GET /api/auth/oauth/:provider/status
路径参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| provider | string | 是 | Provider 名称 |
响应:
{
"bound": true,
"binding": {
"id": 1,
"username": "username"
}
}
或未绑定:
{
"bound": false
}
错误码详解
| 错误码 | HTTP 状态码 | 说明 |
|---|---|---|
OAUTH_PROVIDER_NOT_FOUND |
400 | 请求的 Provider 未注册 |
OAUTH_STATE_INVALID |
400 | state 令牌无效,可能被篡改 |
OAUTH_STATE_EXPIRED |
400 | state 超过 5 分钟有效期 |
OAUTH_TOKEN_EXCHANGE_FAILED |
400 | 与 Provider 交换 access_token 失败 |
OAUTH_USER_INFO_FAILED |
400 | 从 Provider 获取用户信息失败 |
OAUTH_ALREADY_BIND |
400 | 该第三方账号已绑定其他用户 |
OAUTH_BINDING_USER_MISMATCH |
400 | 绑定操作用户与当前登录用户不匹配 |
OAUTH_BINDING_TOKEN_INVALID |
400 | binding token 无效或已过期 |
OAUTH_LAST_BIND |
400 | 无法解绑最后一个 OAuth 账号 |
UNAUTHORIZED |
401 | 需要登录才能进行此操作 |
使用示例
前端调用流程
1. 发起 GitHub 登录
// 方法一:直接跳转(后端处理一切)
window.location.href = '/api/auth/oauth/github/authorize';
// 方法二:打开弹窗
const popup = window.open('/api/auth/oauth/github/authorize', 'oauth', 'width=600,height=700');
2. 回调后处理
// 在登录页或 profile 页检测参数
const url = new URL(window.location.href);
if (url.searchParams.get('oauth_success') === '1') {
// 刷新用户状态
await refreshSession();
// 跳转到首页或用户中心
router.push('/');
}
if (url.searchParams.get('bind_success') === '1') {
// 刷新绑定列表
await fetchBindings();
}
if (url.searchParams.get('oauth_error')) {
const error = url.searchParams.get('oauth_error');
alert(`登录失败: ${error}`);
}
3. 获取绑定列表
const { data } = await useFetch('/api/auth/oauth/bindings');
const bindings = data.value?.bindings ?? [];
4. 解绑操作
await $fetch('/api/auth/oauth/github/unbind', { method: 'DELETE' });
// 刷新绑定列表
await fetchBindings();