Files
beaver_project/auth-portal/src/docs/api.md
2026-03-13 16:40:08 +08:00

15 KiB
Raw Blame History

Nanobot Auth Portal 接口文档

1. 文档范围

本文档覆盖 nanobot-auth-portal 当前实际依赖和对接的接口:

  • Auth Portal 前端页面路由://login/register
  • 后端认证接口:/api/auth/*
  • 登录完成后的浏览器交接接口:目标业务前端 /handoff

说明:

  • Auth Portal 自身是一个独立前端,不在本仓库内实现后端 API。
  • 文档中的后端接口来自当前联调使用的 nanobot-backend 实现。
  • 当前项目只直接调用 POST /api/auth/loginPOST /api/auth/register,但为了便于联调,本文档一并补全了同一条登录链路上的 handoffmelogout 接口。

2. 服务地址与环境变量

2.1 Auth Portal 页面地址

默认运行在当前主机 3081 端口。

2.2 后端 API Base URL

Auth Portal 按以下优先级计算后端地址:

  1. NEXT_PUBLIC_BACKEND_API_URL
  2. 浏览器当前域名 + NEXT_PUBLIC_BACKEND_API_PORT,默认 10000
  3. SSR 回退地址 http://127.0.0.1:10000

示例:

NEXT_PUBLIC_BACKEND_API_URL=https://nanobot-api.bwgdi.com
NEXT_PUBLIC_BACKEND_API_PORT=10000
NEXT_PUBLIC_FRONTEND_PORT=3080

2.3 目标业务前端 Base URL

登录或注册成功后Auth Portal 会从响应里解析目标前端地址,优先级如下:

  1. backend_connection.frontend_base_url
  2. backend_connection.api_base_url
  3. backend_connection.public_base_url
  4. local_backend.public_base_url

如果命中的是后 2 到 4 项Auth Portal 会将端口替换为 NEXT_PUBLIC_FRONTEND_PORT,默认 3080

3. 公共约定

3.1 请求格式

  • 请求体格式:application/json
  • 成功响应JSON
  • Auth Portal 前端请求超时:8000ms

3.2 鉴权方式

  • POST /api/auth/login:无需鉴权
  • POST /api/auth/register:无需鉴权
  • POST /api/auth/handoff/consume:无需鉴权
  • GET /api/auth/me:需要 Authorization: Bearer <access_token>
  • POST /api/auth/logout:可带 Authorization: Bearer <access_token>

3.3 错误响应格式

后端主要返回:

{
  "detail": "错误信息"
}

Auth Portal 前端收到非 2xx 时,会转成如下错误文案:

接口错误 <status>: <detail>

3.4 Token 与 handoff 约定

  • access_token 是后端进程内维护的 Web 会话 token。
  • refresh_token 当前实现始终返回空字符串 ""
  • 当前认证链路没有独立的 /api/auth/refresh
  • handoff_code 是短时有效的浏览器交接码。
  • handoff_code 默认 TTL 为 90 秒。
  • handoff_code 被消费后默认允许 15 秒内的短暂重放窗口,用于前端页面刷新或重试。

4. 数据模型

4.1 BackendConnectionInfo

{
  "backend_id": "backend-001",
  "client_id": "client-001",
  "name": "Boardware Genius",
  "public_base_url": "https://nanobot-api.example.com",
  "api_base_url": "https://nanobot-api.example.com",
  "ws_base_url": "wss://nanobot-api.example.com",
  "frontend_base_url": "https://nanobot.example.com",
  "registered": true
}

字段说明:

  • backend_idbackend 唯一标识
  • client_idbackend 在 AuthZ 中的客户端 ID
  • namebackend 名称
  • public_base_url:公开后端地址
  • api_base_url:业务 API 地址
  • ws_base_urlWebSocket 地址
  • frontend_base_url:目标业务前端入口地址
  • registered:当前 backend 是否已完成本地身份注册

4.2 AuthzLocalBackendStatus

{
  "backend_id": "backend-001",
  "client_id": "client-001",
  "name": "Boardware Genius",
  "public_base_url": "https://nanobot-api.example.com",
  "authz": {
    "enabled": true,
    "base_url": "https://authz.example.com"
  }
}

字段说明:

  • backend_id:本地 backend identity 中记录的 backend ID
  • client_id:本地 backend identity 中记录的 client ID
  • name:本地 backend 名称
  • public_base_url:本地 backend 对外地址
  • authz.enabled:是否启用 AuthZ
  • authz.base_url:当前 backend 使用的 AuthZ 服务地址

4.3 RegisterAuthzStatus

POST /api/auth/register 响应会返回。

{
  "enabled": true,
  "base_url": "https://authz.example.com",
  "user_registered": true,
  "backend_registered": true
}

字段说明:

  • enabled:本次注册是否启用了 AuthZ
  • base_url:本次注册使用的 AuthZ 服务地址
  • user_registered:用户是否已在 AuthZ 完成注册或确认存在
  • backend_registeredbackend 身份是否已在 AuthZ 完成注册

4.4 TokenResponse

登录、注册、handoff 消费都会返回 token 响应,但字段不完全相同。

{
  "access_token": "opaque-token",
  "refresh_token": "",
  "token_type": "bearer",
  "user_id": "bwgdi",
  "username": "bwgdi",
  "email": "steven@example.com",
  "role": "owner",
  "handoff_code": "short-lived-code",
  "handoff_expires_at": 1760000000,
  "existing_user": false,
  "authz": {
    "enabled": true,
    "base_url": "https://authz.example.com",
    "user_registered": true,
    "backend_registered": true
  },
  "backend_connection": {
    "backend_id": "backend-001",
    "client_id": "client-001",
    "name": "Boardware Genius",
    "public_base_url": "https://nanobot-api.example.com",
    "api_base_url": "https://nanobot-api.example.com",
    "ws_base_url": "wss://nanobot-api.example.com",
    "frontend_base_url": "https://nanobot.example.com",
    "registered": true
  },
  "local_backend": {
    "backend_id": "backend-001",
    "client_id": "client-001",
    "name": "Boardware Genius",
    "public_base_url": "https://nanobot-api.example.com",
    "authz": {
      "enabled": true,
      "base_url": "https://authz.example.com"
    }
  }
}

字段说明:

  • access_token:后端签发的登录 token
  • refresh_token:当前固定为空字符串
  • token_type:当前固定为 bearer
  • user_id:当前等于用户名
  • username:用户名
  • email:仅注册响应会返回,登录响应通常没有
  • role:当前固定为 owner
  • handoff_code:仅登录/注册响应返回,用于跳转到目标前端
  • handoff_expires_atUNIX 时间戳,单位秒
  • existing_user:仅注册响应返回,表示这次注册是否命中了已有用户
  • authz:仅注册响应返回,表示本次 AuthZ 注册结果
  • backend_connection:当前登录用户对应的目标 backend 路由信息
  • local_backend:本地 backend identity 视图

4.5 AuthUser

GET /api/auth/me 返回:

{
  "id": "bwgdi",
  "username": "bwgdi",
  "email": "",
  "role": "owner",
  "quota_tier": "single-user"
}

5. 前端页面路由

5.1 GET /

用途Portal 首页。

行为:立即重定向到 /login

5.2 GET /login

用途:显示登录页面。

Query 参数:

  • next:登录成功后希望进入的目标业务前端路径,默认 /

示例:

/login?next=/mcp

成功后浏览器会跳转到:

<frontend_base_url>/handoff?code=<handoff_code>&next=/mcp

5.3 GET /register

用途:显示注册页面。

Query 参数:

  • next:注册成功后希望进入的目标业务前端路径,默认 /mcp

示例:

/register?next=/mcp

6. 后端认证接口

6.1 POST /api/auth/login

用途:用户名密码登录,并返回目标 backend 路由与 handoff 信息。

鉴权:否。

请求体:

{
  "username": "bwgdi",
  "password": "123456"
}

请求字段:

  • username:必填,登录用户名
  • password:必填,登录密码

成功响应:200 OK

{
  "access_token": "opaque-token",
  "refresh_token": "",
  "token_type": "bearer",
  "user_id": "bwgdi",
  "username": "bwgdi",
  "role": "owner",
  "handoff_code": "short-lived-code",
  "handoff_expires_at": 1760000000,
  "backend_connection": {
    "backend_id": "backend-001",
    "client_id": "client-001",
    "name": "Boardware Genius",
    "public_base_url": "https://nanobot-api.example.com",
    "api_base_url": "https://nanobot-api.example.com",
    "ws_base_url": "wss://nanobot-api.example.com",
    "frontend_base_url": "https://nanobot.example.com",
    "registered": true
  },
  "local_backend": {
    "backend_id": "backend-001",
    "client_id": "client-001",
    "name": "Boardware Genius",
    "public_base_url": "https://nanobot-api.example.com",
    "authz": {
      "enabled": true,
      "base_url": "https://authz.example.com"
    }
  }
}

错误码:

  • 400 Bad Requestusername 为空
  • 401 Unauthorized:用户名或密码错误
  • 500 Internal Server Error:本地用户文件不存在或格式非法

实现备注:

  • 登录成功后Portal 必须使用 handoff_code 跳到目标业务前端 /handoff
  • 如果后端没有返回 frontend_base_urlhandoff_codePortal 会直接提示错误,不会继续跳转。

6.2 POST /api/auth/register

用途:创建本地登录账号,并在需要时完成 AuthZ 用户/后端注册,然后返回 handoff 信息。

鉴权:否。

当前 Portal 页面实际发送字段:

{
  "username": "bwgdi",
  "email": "steven@example.com",
  "password": "123456"
}

后端支持的完整请求体:

{
  "username": "bwgdi",
  "email": "steven@example.com",
  "password": "123456",
  "authz_base_url": "https://authz.example.com",
  "backend_name": "Boardware Genius",
  "backend_id": "backend-001",
  "base_url": "https://nanobot-api.example.com",
  "frontend_base_url": "https://nanobot.example.com"
}

请求字段:

  • username:必填,用户名
  • email:选填,邮箱
  • password:必填,密码
  • authz_base_url:选填,覆盖后端当前配置的 AuthZ 地址
  • backend_name:选填,注册 backend 时使用的名称
  • backend_id:选填,期望写入或复用的 backend ID
  • base_url:选填,注册 backend 时使用的公开 API 地址
  • frontend_base_url:选填,注册 backend 时使用的公开前端地址

成功响应:当前实现为 200 OK

{
  "access_token": "opaque-token",
  "refresh_token": "",
  "token_type": "bearer",
  "user_id": "bwgdi",
  "username": "bwgdi",
  "email": "steven@example.com",
  "role": "owner",
  "handoff_code": "short-lived-code",
  "handoff_expires_at": 1760000000,
  "existing_user": false,
  "authz": {
    "enabled": true,
    "base_url": "https://authz.example.com",
    "user_registered": true,
    "backend_registered": true
  },
  "backend_connection": {
    "backend_id": "backend-001",
    "client_id": "client-001",
    "name": "Boardware Genius",
    "public_base_url": "https://nanobot-api.example.com",
    "api_base_url": "https://nanobot-api.example.com",
    "ws_base_url": "wss://nanobot-api.example.com",
    "frontend_base_url": "https://nanobot.example.com",
    "registered": true
  },
  "local_backend": {
    "backend_id": "backend-001",
    "client_id": "client-001",
    "name": "Boardware Genius",
    "public_base_url": "https://nanobot-api.example.com",
    "authz": {
      "enabled": true,
      "base_url": "https://authz.example.com"
    }
  }
}

错误码:

  • 400 Bad Requestusernamepassword 缺失
  • 409 Conflict:用户名已存在,且提交的密码与已有密码不一致
  • 4xx/5xxAuthZ 上游服务直接返回的错误
  • 502 Bad Gateway:调用 AuthZ 服务失败

实现备注:

  • 如果用户名已存在但密码相同,后端会把本次请求视为“继续完成配置”,不会直接报错。
  • 如果本地 web_auth_users.json 不存在,注册会自动创建。
  • 注册成功后同样必须依赖 handoff_code 进入目标业务前端。

6.3 POST /api/auth/handoff/consume

用途:由目标业务前端消费 handoff_code,换取可直接使用的 access_token

鉴权:否。

请求体:

{
  "code": "short-lived-code"
}

成功响应:200 OK

{
  "access_token": "opaque-token",
  "refresh_token": "",
  "token_type": "bearer",
  "user_id": "bwgdi",
  "username": "bwgdi",
  "role": "owner"
}

错误码:

  • 400 Bad Requestcode 为空
  • 401 Unauthorizedcode 无效、已失效,或 payload 非法
  • 410 Gonecode 已过期,或已超过重放窗口

实现备注:

  • 当前目标前端在 /handoff 页面里调用该接口。
  • 目标前端拿到 token 后会继续调用 GET /api/auth/me 校验登录态。

6.4 GET /api/auth/me

用途:获取当前登录用户信息。

鉴权:是,必须提供 Authorization: Bearer <access_token>

请求头:

Authorization: Bearer opaque-token

成功响应:200 OK

{
  "id": "bwgdi",
  "username": "bwgdi",
  "email": "",
  "role": "owner",
  "quota_tier": "single-user"
}

错误码:

  • 401 Unauthorized:缺少 Authorization
  • 401 UnauthorizedAuthorization 头格式错误
  • 401 Unauthorizedtoken 为空
  • 401 Unauthorizedtoken 无效或已失效

6.5 POST /api/auth/logout

用途:注销当前 token。

鉴权:可选。带 token 时会尝试从后端内存中删除该 token。

请求头:

Authorization: Bearer opaque-token

成功响应:200 OK

{
  "ok": true
}

实现备注:

  • 即使没有传 token接口也会返回 {"ok": true}
  • Portal 或业务前端通常还会同步清理浏览器本地 token。

7. 登录与跳转链路

7.1 登录链路

  1. 用户访问目标业务前端的受保护页面,例如 /mcp
  2. 业务前端发现未登录,跳转到 Auth Portal/login?next=/mcp
  3. Auth Portal 提交 POST /api/auth/login
  4. 后端返回 handoff_codebackend_connection.frontend_base_url
  5. Auth Portal 跳转到目标业务前端:
https://target-frontend.example.com/handoff?code=<handoff_code>&next=/mcp
  1. 目标业务前端在 /handoff 页面调用 POST /api/auth/handoff/consume
  2. 目标业务前端保存 token 后调用 GET /api/auth/me
  3. 用户最终进入 /mcp

7.2 注册链路

  1. 用户访问 Auth Portal/register?next=/mcp
  2. Auth Portal 提交 POST /api/auth/register
  3. 后端按需创建本地用户,并与 AuthZ 同步用户/后端身份
  4. 后端返回 handoff_code 与目标前端地址
  5. Portal 跳转到目标业务前端 /handoff
  6. 后续步骤与登录链路一致

8. 联调示例

8.1 登录

curl -X POST 'https://nanobot-api.example.com/api/auth/login' \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "bwgdi",
    "password": "123456"
  }'

8.2 消费 handoff_code

curl -X POST 'https://nanobot-api.example.com/api/auth/handoff/consume' \
  -H 'Content-Type: application/json' \
  -d '{
    "code": "short-lived-code"
  }'

8.3 获取当前用户

curl 'https://nanobot-api.example.com/api/auth/me' \
  -H 'Authorization: Bearer opaque-token'

9. 当前实现限制

  • refresh_token 目前没有实际用途,返回值固定为空字符串。
  • token 与 handoff code 都保存在后端进程内存中,后端重启后会失效。
  • 本仓库前端页面目前没有暴露 register 的高级可选字段,只使用基础注册参数。
  • GET /api/auth/me 当前返回的 email 固定为空字符串,不会回填注册邮箱。