Files

12 KiB

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

账号、注册与单点总开关(2026-10-05)

管理员设置 GET/PUT /api/system-settings/authentication,请求和响应是完整对象:

{"password_login_enabled":false,"registration_enabled":false,"sso_enabled":true,"single_session_enabled":false}

密码登录页面、自助注册与同账号单处登录默认关闭,外部系统免登默认开启,已有管理员保存的设置不变。至少保留一种登录方式;自助注册要求密码登录开启。无个人密码的存量用户使用原共享口令,未配置则使用 123ewq,因此关闭外部系统免登无需先给管理员设个人密码。未登录页面通过 GET /api/v1/auth/options 读取同四字段和 needs_setup(当前没有管理员),响应禁止缓存。这里只公开开关,不公开共享密码、个人密码或上游密钥。

接口 请求 成功响应
POST /api/v1/auth/login/local form-urlencoded username、password;用户名或邮箱 UsernameLoginResponse + HttpOnly Cookie
POST /api/v1/auth/register JSON {username,password};兼容 {email,password},两种账号字段只传一项 201 + UsernameLoginResponse,自动登录普通用户
POST /api/v1/auth/initialize JSON {email,password};只允许系统没有管理员时 201 + UsernameLoginResponse,创建首任管理员
GET /api/v1/auth/me 当前会话 id,email,system_role,needs_setup,has_password,requires_current_password,uses_default_password
POST /api/v1/auth/change-password JSON {current_password,new_password} message,access_token,token_type,expires_in + 更新 Cookie
PUT /api/v1/auth/users/{id}/login-password 管理员 JSON {password};也可用 /reset-password 别名 用户列表项;重置自己时附 access_token,token_type,expires_in

UsernameLoginResponse 为 access_token,token_type,expires_in,user_id,email,system_role,needs_setup,created,username。前端收到改密/本人重置成功响应后必须同步替换本地 Bearer,否则会被旧版本 token 拒绝。新增 auth_usernames 表用数据库唯一键保护并发新建账号的用户名;新旧域名注册同名也只能成功一次。ORM 自动创建该表,历史用户在新建前按邮箱 local-part 检查。用户名单点在初次查询后发生并发注册/改密时,会再次校验最终账号凭据后才签发会话。

新密码为 8–255 字符,拒绝常见弱密码和首尾空格;所有用户都必须提供 current_password,无个人凭据时用原共享口令(未配置则 123ewq)首次设置,不能留空。用户名接受字母、数字、.、_、-,首尾须为字母或数字,也接受有效邮箱。用户名不区分大小写;已存在的相同邮箱 local-part 不得通过换域名重复注册或覆盖。重复返回 409,校验失败 422,关闭方式或越权返回 403,错误密码 401(改密码的错误当前密码为 400),登录限速 429。新注册沿用现有黑名单语义默认放行;已有封禁账号仍被拦截。

新密码与「登录口令」统一在 password_hash、login_password 保存同一版本化散列;两种登录入口接受同一密码。历史明文个人口令优先于旧 password_hash,可直接用于密码登录、URL 口令登录或验证改密,修改后即改存散列。管理员列表仅对历史明文保留旧兼容,任何散列均不返回,新增 has_login_password 表示是否已设置。管理员设置/重置不回显密码;已有密码不得清空。复制新登录链接时使用本次输入,之后需重新输入或重置。修改/重置原子递增 token_version,此前全部会话与旧密码失效;并发过期修改返回 409,普通审批不能覆写凭据或复活旧会话。

sso_enabled=false 后,用户名直登、上游 token、OAuth 及 /api/parallel-agents/auth/token 都拒绝登录。并行面板兼容入口现复用标准用户名登录的密码、限速和封禁校验,可附 ?password=。关闭自助注册不影响已启用的单点对外部身份自动建普通用户。用户名含 admin 不再授予或提升管理员权限;历史已经是管理员的账号不变。首任管理员初始化使用独立单例 claim 与用户插入的同一数据库事务,跨工作进程并发也最多成功一次。部署无需人工增加列,ORM 启动自动创建 auth_initialization 表;管理员恢复仍使用 python -m app.gateway.auth.reset_admin,同样同步两套凭据。

启用 password_login_enabled 后,用户名/并行面板登录也必须验证个人密码或原共享口令(未配置 auth_login.username_login.password 时为 123ewq);个人凭据存在时共享口令不能替代。JWT 的 auth_method=password/passphrase/sso 仅由实际完成校验的登录路径显式写入。Gateway 与 LangGraph 同时拒绝缺少标记的历史/免密会话,要求重新登录;已验证的上游 SSO 仍受独立 sso_enabled 控制。开发环境 DEER_FLOW_AUTH_DISABLED 也不能绕过已开启的密码登录或单处登录限制。关闭密码登录时保留旧用户名单点及共享口令门行为;create_access_token 不会自动给未经校验的调用添加可信标记。

「单点登录(同账号仅一处在线)」由独立 single_session_enabled 控制,默认关闭;sso_enabled 仅表示既有外部单点/免登入口,两个功能分开。开启单处登录后,每次成功的账号密码、用户名口令、上游 token、并行面板、注册或初始化登录,都在数据库内原子递增该账号 token_version 后签新 JWT,旧会话立即返回 401,前端轮询 /me 后退出旧页面。失败登录不会踢人,关闭时允许多处会话并存;修改/重置密码仍始终撤销旧会话。并发登录通过同一行的原子递增保证最终仅最高版本会话有效;事务还比较已验证的密码/口令与权限快照,避免并发重置密码后旧凭据借新登录版本恢复。

在原有登录之上新增两种自定义登录模式,均由 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 自动重载)。


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

此前提为系统设置的 sso_enabled=true。账号已设置个人密码/口令时,始终校验该凭据;未设置个人凭据时,开启账号密码登录或 require_password 任一开关都会要求共享口令(未配置则为 123ewq)。

请求

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

{ "username": "alice" }
  • ?password=:账号已有个人密码/口令时必须匹配个人凭据;否则仅当 require_password=true 时需要匹配共享口令。
  • 共享口令仅适用于没有个人凭据的账号,不影响目标账号选择。
  • 校验失败按 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