deerflow-code/offline-backend-20260512/backend/docs/AUTH_LOGIN.md
2026-09-07 18:24:55 +08:00

5.5 KiB

登录鉴权扩展接口文档(auth_login)

在原有登录之上新增两种自定义登录模式,均由 config.yaml 的 auth_login 段控制。

配置

auth_login:
  # 功能一:用户名直登口令门
  username_login:
    require_password: false   # true 时 /login/username 必须带 ?password=
    password: ""              # 共享口令;留空默认 123ewq
  # 功能二:token 换登录
  token_login:
    enabled: false                                                   # 总开关
    token_info_url: "https://ch1.b.uat.4.cn/consumer/login/getTokenInfo"
    service_authorization: "Bearer admin"                            # 调上游接口的鉴权头
    timeout_seconds: 10
    verify_ssl: false   # 上游 HTTPS 证书校验;UAT/内网自签证书需置 false,生产置 true
    ca_cert_path: ""    # 可选:PEM 格式 CA 包路径,设置后优先生效并强制校验

HTTPS 注意:token_info_url 是 HTTPS。若上游用自签名 / 私有 CA 证书(UAT、内网常见),verify_ssl: true 时 httpx 的 TLS 握手会失败,导致 token 登录恒定 401。此时应:

  • 临时/内网:verify_ssl: false(跳过证书校验);
  • 正规做法:把私有 CA 证书导出为 PEM,配 ca_cert_path: /path/to/ca.pem(优先于 verify_ssl,仍做校验);
  • 生产对外域名(有效证书):保持 verify_ssl: true。

握手失败时后端日志会打印具体异常类型(如 SSLError)与当前 verify 取值,便于排查。

配置模型:packages/harness/deerflow/config/login_config.py。修改 config.yaml 后无需重启(get_app_config() 按 mtime 自动重载)。


功能一:用户名直登 + 口令门

在原 POST /api/v1/auth/login/username 上叠加一个共享口令校验。require_password=false(默认)时行为完全不变(免密直登 + 自动注册)。

请求

POST /api/v1/auth/login/username[?password=<共享口令>]
Content-Type: application/json

{ "username": "alice" }
  • ?password=:仅当 require_password=true 时需要。值需等于 username_login.password;该项留空时默认口令为 123ewq。
  • 口令门是单一共享口令,只决定"放不放行",不影响登录成的是哪个账号。
  • 校验失败按 IP 计入登录限速(5 次/5 分钟锁定),与本地密码登录共用同一限速器。

响应

成功 200:

{
  "access_token": "<JWT>",
  "token_type": "bearer",
  "expires_in": 604800,
  "user_id": "<uuid>",
  "email": "alice@cm.com",
  "system_role": "user",
  "needs_setup": false,
  "created": false
}

同时种下 access_token HttpOnly Cookie。

失败:

状态码 场景
401 require_password=true 但口令缺失或不匹配
429 触发登录限速

前端入口

访问 /login/<用户名>?password=<口令>。LoginPage.tsx 从 ?password= 读取并透传给 loginByUsername。


功能二:token 换登录

前端用 URL 把上游 token 传进来,后端调上游接口换出用户名再登录。

请求

POST /api/v1/auth/login/token?token=<上游access_token>

后端据此向 token_info_url 发起(与下方 curl 对齐):前端传入的 token 直接拼成 Authorization: Bearer <token> 头,上游从该头读取令牌(不再用 cookie,也不再用静态 service_authorization)。

curl 'https://ch1.b.uat.4.cn/consumer/login/getTokenInfo' \
  -X POST \
  -H 'authorization: Bearer <上游access_token>' \
  -H 'content-type: application/x-www-form-urlencoded'

上游返回示例:

{ "state": "200", "msg": "操作成功!", "data": { "username": "b1admin", "...": "..." } }

后端取 data.username → 经 _username_to_email 映射 → 库内查找(含跨域名 local-part 兜底)→ 查不到则自动注册 → 发本系统 JWT 并种 Cookie。

响应

成功 200:结构同功能一的 UsernameLoginResponse(首次自动注册时 created=true)。

失败:

状态码 场景
403 token_login.enabled=false
401 上游调用失败 / HTTP 非 200 / state != "200" / 未取到 username
429 触发登录限速
500 token_info_url 未配置

前端入口

访问 /login?authToken=<上游token>(用 authToken 而非 token,以避开既有的 tools_token 参数 ?token=)。LoginPage 命中 authToken 时走 loginByToken,不再调用户名直登。


安全说明

  • 两个端点都在 auth_middleware._PUBLIC_EXACT_PATHS 白名单内(免会话鉴权,自身完成鉴权)。
  • 口令门是最低限度的共享密钥防护,适合内网/受控环境;非每用户独立密码。
  • token 换登录信任上游 getTokenInfo 的鉴权结论,务必保证 token_info_url 配置正确且走内网/HTTPS。前端传入的 token 会直接作为 Authorization: Bearer <token> 发给上游,因此该 token 必须来自可信入口。(service_authorization 配置项已不再参与请求,保留仅为兼容,后续可移除。)

相关代码

  • 后端端点 / 助手:app/gateway/routers/auth.py(login_by_username、login_by_token、_get_or_create_user_by_username、_fetch_username_from_token)
  • 白名单:app/gateway/auth_middleware.py
  • 配置模型:packages/harness/deerflow/config/login_config.py(挂载于 AppConfig.auth_login)
  • 前端:frontend-web/src/core/auth/api.ts、frontend-web/src/pages/LoginPage.tsx
  • 测试:tests/test_login_config.py、tests/test_auth_login_modes.py