ClouisleClouisle

认证与用户登录 API

用户注册、人机验证、密码登录、TOTP 两步验证、密码找回与邮箱验证接口

认证与登录接口提供账户注册、点击式人机验证、OAuth2 兼容的密码登录、TOTP 两步验证挑战与校验、密码重置与邮箱验证。基础路径为 /api/v1。

TOTP 的绑定与解绑在独立的 /api/v1/totp/* 端点上(见 TOTP API),SSO 在 /api/v1/sso/*(见 SSO API)。本页聚焦登录链路本身,以及登录时如何使用这两者。

端点总览

方法路径用途认证方式
GET/api/v1/captcha获取基于点击的人机验证挑战公开
POST/api/v1/captcha/click提交点击轨迹,换取一次性验证凭证公开
POST/api/v1/login/access-token用户名/邮箱 + 密码登录公开
POST/api/v1/login/verify-totp用临时令牌 + 动态码换取正式令牌公开(需 temp_token)
POST/api/v1/logout注销当前会话并将令牌加入黑名单Bearer JWT
POST/api/v1/register注册新账户公开
POST/api/v1/send-verification发送邮箱验证码/链接公开
POST/api/v1/verify-email用 6 位验证码验证邮箱公开
GET/api/v1/verify用邮件链接中的令牌验证邮箱公开
POST/api/v1/resend-verification重新发送注册验证邮件公开
POST/api/v1/forgot-password请求找回密码邮件公开
POST/api/v1/reset-password用验证码或令牌设置新密码公开

令牌与会话

登录成功返回 JWT,请求时放在 Authorization: Bearer <token>。

项目值
有效期站点设置 session_timeout_days,默认 30 天(设置行缺失时代码回退值为 7 天)
刷新机制无 refresh token,过期后需重新登录
注销POST /api/v1/logout 将令牌写入黑名单,立即失效
单会话模式single_session = true 时,新登录会使旧令牌失效,旧令牌请求返回 HTTP 401 / 2001
注销失败未携带令牌时返回 HTTP 401 / 2000

人机验证(Captcha)

站点开启 enable_captcha(默认 false)时,注册、密码登录、找回密码都需要携带人机验证凭证。

1. 获取验证挑战

GET /api/v1/captcha
{
  "code": 0,
  "data": {
    "captcha_id": "c62040db-91fa-4ff8-9125-5e3681428256",
    "challenge": "{\"target_text\": \"请点击红色的圆圈\", \"image\": \"data:image/png;base64,...\"}",
    "prompt": "captcha_click_prompt",
    "expires_in": 300
  },
  "msg": "success"
}

challenge 是一个 JSON 字符串(需自行解析),expires_in 为 300 秒。

2. 提交点击换取凭证

POST /api/v1/captcha/click
Content-Type: application/json

{
  "captcha_id": "c62040db-91fa-4ff8-9125-5e3681428256",
  "challenge": "{\"target_text\": \"请点击红色的圆圈\", \"image\": \"data:image/png;base64,...\"}",
  "clicked_option": "red_circle",
  "elapsed_ms": 1340,
  "pointer": [
    {"x": 105.5, "y": 80.2, "t": 1200, "event": "move"},
    {"x": 108.0, "y": 82.0, "t": 1340, "event": "move"}
  ]
}
字段类型必填说明
captcha_idstring是挑战返回的 ID
challengestring是原样回传挑战字符串
clicked_optionstring是用户选择的选项标识
elapsed_msinteger是从展示到点击的耗时(毫秒)
pointerarray否指针轨迹点,每项含 x、y、t,可选 event(默认 move);空数组也可以
{
  "code": 0,
  "data": {
    "captcha_id": "c62040db-91fa-4ff8-9125-5e3681428256",
    "captcha_token": "proof-token-string"
  },
  "msg": "success"
}

凭证一次性使用:同一套 captcha_id + captcha_token 不能重复换取登录。

HTTPcode场景
4005302需要人机验证但未携带 captcha_id / captcha_token
4005303凭证无效、已使用或已过期;或提交的轨迹未通过校验

密码登录与两步验证

1. 提交凭证登录

标准 OAuth2 表单(application/x-www-form-urlencoded,不是 JSON):

POST /api/v1/login/access-token
Content-Type: application/x-www-form-urlencoded

username=alice@example.com&password=SecurePassword123!&captcha_id=c62040db-...&captcha_token=proof-token-string
表单字段类型必填说明
usernamestring是用户名或邮箱;后端字段名为 identifier,通过别名接收
passwordstring是密码
captcha_idstring开启人机验证时必填验证挑战 ID
captcha_tokenstring开启人机验证时必填点击凭证
captcha_answerstring否旧客户端兼容字段:captcha_token 为空时用它作为凭证兜底

登录前的检查顺序(决定了你会看到哪个错误):

  1. 站点关闭密码登录(sso_enabled = true 且 sso_allow_password_login = false)→ 400 / 6306。
  2. 账号被锁定 → 400 / 5300,data.lockout_seconds 给出剩余秒数。
  3. 密码错误 → 400 / 2003,data.remaining_attempts 给出剩余尝试次数;失败次数达到 max_login_attempts(默认 5)会按 lockout_duration_minutes(默认 15 分钟)锁定并返回 5300。
  4. 账号停用或待审批 → 400 / 2004(approval_status = pending 时提示待审批)。
  5. 已开启 TOTP → 返回 requires_totp(见下)。
  6. 站点强制 TOTP(require_totp = true)而用户未绑定 → 返回 requires_totp_setup。
  7. 开启邮箱验证(email_verification 默认 true)且用户未验证(超级管理员除外)→ 400 / 5004,data.email 给出邮箱。

直接登录成功(200 OK)

{
  "code": 0,
  "data": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer" },
  "msg": "Login successful"
}

要求两步验证(TOTP 挑战)

账号已开启 TOTP 时不发放正式令牌,而是返回有效期 5 分钟的临时令牌:

{
  "code": 0,
  "data": { "requires_totp": true, "temp_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." },
  "msg": "totp_required"
}

要求先绑定两步验证

站点开启 require_totp 且账号未绑定时,返回有效期 30 分钟的临时令牌,用于先调用 /api/v1/totp/setup 完成绑定:

{
  "code": 0,
  "data": { "requires_totp_setup": true, "temp_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." },
  "msg": "totp_setup_required"
}

这三种都是成功响应(code: 0),不是错误。客户端必须先判断 data 中的分支标志(requires_totp / requires_totp_setup / force_password_change),再决定跳转到哪一步。

强制修改密码

密码过期或管理员标记必须修改时,仍发放正式令牌,但带标记:

{
  "code": 0,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "bearer",
    "force_password_change": true,
    "reason": "expired"
  },
  "msg": "Login successful. You must change your password before continuing."
}

reason 为 expired(密码过期)或 force(管理员强制)。此时应引导用户调用 POST /api/v1/users/me/change-password。

2. 校验 TOTP 完成登录

POST /api/v1/login/verify-totp
Content-Type: application/x-www-form-urlencoded

temp_token=temp-jwt-token-string&code=123456&is_backup_code=false
表单字段类型必填默认值说明
temp_tokenstring是—上一步返回的临时 JWT
codestring是—身份验证器生成的 6 位动态码,或使用 is_backup_code=true 时提交备用恢复码
is_backup_codeboolean否false是否按备用恢复码校验

全部字段都是 form 字段;用 JSON body 提交会得到 422。

成功时按与登录相同的规则处理强制改密,并返回:

{
  "code": 0,
  "data": { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer" },
  "msg": "Two-factor authentication verified successfully"
}

失败路径:

HTTPcode场景
4002001临时令牌无效或已被篡改
4002002临时令牌已过期(totp_setup_expired)
4005313该账号未开启 TOTP(或密钥缺失)
4005312连续失败次数过多被限速,data.seconds 为剩余等待秒数
4005311动态码或备用码不正确

使用备用恢复码成功校验后,该码立即失效,剩余可用数量会记入审计日志。

3. 注销登录

POST /api/v1/logout
Authorization: Bearer YOUR_TOKEN

将当前令牌加入黑名单,响应 data: null,msg 为 Logout successful。注销后必须重新登录。

用户注册与邮箱验证

1. 注册新账户

POST /api/v1/register
Content-Type: application/json

{
  "username": "bob",
  "email": "bob@example.com",
  "password": "StrongPassword123!",
  "locale": "zh",
  "terms_accepted": true,
  "captcha_id": "c62040db-...",
  "captcha_token": "proof-token-string"
}
字段类型必填说明
usernamestring是用户名,唯一
emailstring是邮箱,唯一
passwordstring是密码,按密码策略校验
localestring否界面语言
terms_acceptedboolean站点要求时必填站点开启 require_terms_acceptance_on_register 时必须为 true,否则 1001
captcha_id / captcha_tokenstring站点开启人机验证时必填见人机验证小节

注册结果由站点设置决定,msg 与账号状态随设置变化:

场景approval_statusis_activeemail_verifiedmsg
系统第一个用户approvedtruetrueRegistration successful. You are the first user and have been promoted to Super Admin!
require_approval = true(默认)pendingfalse视邮箱验证设置Registration successful. Your account is pending admin approval.
require_approval = false 且 email_verification = true(默认)approvedtruefalseRegistration successful. Please verify your email to activate your account.
两者都关闭approvedtruetrueRegistration successful

require_approval 默认为 true,因此默认注册不会直接可用:新账号需要管理员在后台审批(或用户完成邮箱验证)后才能登录,如未完成,登录会返回 2004(待审批)或 5004(邮箱未验证)。系统第一个注册的用户跳过上述全部限制:直接激活、免邮箱验证并自动获得超级管理员角色;其余新用户会被分配默认角色与默认团队。

失败路径:用户名重复 5002;邮箱重复 5003;密码不符合策略 1001(data.errors.password 列出原因);开放注册关闭 5000;人机验证缺失/失败 5302/5303。

2. 发送验证邮件

POST /api/v1/send-verification
Content-Type: application/json

{ "email": "bob@example.com", "purpose": "register" }
字段类型必填默认值说明
emailstring是—收件邮箱
purposestring否registerregister 或 reset_password

行为差异:

  • purpose = register:邮箱对应的用户必须存在且未验证;不存在返回 400 / 4000,已验证返回 400 / 1001。
  • purpose = reset_password:无论邮箱是否存在都返回成功(防枚举);存在时发送重置邮件。

同一邮箱同一用途有 60 秒冷却,冷却内重复请求返回 400 / 5008,data.remaining_seconds 为剩余秒数。SMTP 未启用时返回 400 / 5007。邮件在后台任务发送,响应只表示已入队。

3. 用 6 位验证码验证邮箱

POST /api/v1/verify-email
Content-Type: application/json

{ "email": "bob@example.com", "code": "839201", "purpose": "register" }
字段类型必填默认值
emailstring是—
codestring是—
purposestring否register

purpose = register 时会把用户的 email_verified 置为 true。

{ "code": 0, "data": { "verified": true, "email": "bob@example.com" }, "msg": "Email verified successfully" }

验证码错误或已被消费返回 400 / 5005。

4. 用邮件链接验证邮箱

GET /api/v1/verify?token=email-verification-token-string

token 是查询参数。令牌无效或已过期返回 400 / 5006,其余成功响应同验证码方式。

5. 重新发送注册验证邮件

POST /api/v1/resend-verification
Content-Type: application/json

{ "email": "bob@example.com" }

与 send-verification 的 register 用途不同,本端点对不存在的邮箱也返回成功(防枚举),但邮箱已通过验证时返回 400 / 1001。同样受 60 秒冷却(5008)与 SMTP 开关(5007)约束。

密码找回与重置

1. 发起找回密码

POST /api/v1/forgot-password
Content-Type: application/json

{ "email": "alice@example.com" }

为防邮箱枚举,无论邮箱是否存在都返回成功(msg 为 If the email exists, a password reset link has been sent)。但当 SMTP 未启用时返回 400 / 5007,60 秒冷却内重复请求返回 400 / 5008 —— 这两种情况下攻击者能推断出部署状态,请知悉这是刻意取舍(可用性优先)。

2. 确认重置密码

支持两种互斥的方式,至少提供一种(都不提供返回 422):

POST /api/v1/reset-password
Content-Type: application/json

{ "email": "alice@example.com", "code": "839201", "new_password": "NewStrongPassword456!" }
POST /api/v1/reset-password
Content-Type: application/json

{ "token": "reset-token-string", "new_password": "NewStrongPassword456!" }
字段类型必填说明
email + codestring二选一6 位验证码方式,purpose 固定为 reset_password
tokenstring二选一邮件链接令牌方式
new_passwordstring是新密码,按密码策略校验

重置成功后会清空该账号的连续失败次数与锁定时间(failed_login_attempts、locked_until),因此被锁定的用户可以通过重置密码立即恢复登录。

失败路径:验证码/令牌无效 400 / 5005;用户不存在 400 / 4000;新密码不符合策略 400 / 1001。

两步验证与 SSO 的边界

登录流程只负责「挑战与校验」,后续管理在其他页面:

需求端点详见
绑定 TOTP、生成备用码、查询状态、解绑POST /api/v1/totp/setup、/enable、/disable、/regenerate-backup-codes、GET /api/v1/totp/statusTOTP API
列出公开 SSO 提供商、发起 SSO 登录、处理回调GET /api/v1/sso/providers、/sso/login/{provider_name}、/sso/callback/{provider_name}SSO API
解除本人已绑定的 SSO 连接DELETE /api/v1/sso/connections/{connection_id}SSO API

站点启用 SSO 并关闭密码登录(sso_allow_password_login = false)后,POST /api/v1/login/access-token 会直接返回 400 / 6306。客户端应先调用 GET /api/v1/sso/providers 判断可用登录方式,再决定是否显示密码表单。

错误码

HTTPcode常量场景
4012000UNAUTHORIZED注销未携带令牌
4002001INVALID_TOKENtemp_token 无效或已被篡改
4002002TOKEN_EXPIRED临时令牌过期(totp_setup_expired)
4002003INVALID_CREDENTIALS用户名或密码错误
4002004INACTIVE_USER账号已停用或待审批
4005000REGISTRATION_DISABLED开放注册已关闭
4005002USERNAME_EXISTS用户名已被占用
4005003EMAIL_EXISTS邮箱已被占用
4005004EMAIL_NOT_VERIFIED登录前必须完成邮箱验证
4005005VERIFICATION_CODE_INVALID验证码或重置令牌无效
4005006VERIFICATION_CODE_EXPIRED验证码或邮件令牌已过期
4005007EMAIL_SEND_FAILEDSMTP 未配置,无法发送邮件
4005008EMAIL_SEND_TOO_FREQUENT60 秒冷却内重复发信
4005300ACCOUNT_LOCKED连续失败次数超限被锁定,data.lockout_seconds 为剩余秒数
4005302CAPTCHA_REQUIRED需要携带人机验证凭证
4005303CAPTCHA_INVALID人机验证凭证无效或已使用
4005311TOTP_INVALID动态码或备用码错误
4005312TOTP_RATE_LIMITEDTOTP 连续失败被限速,data.seconds 为剩余秒数
4005313TOTP_NOT_ENABLED该账号未开启两步验证
4005315TOTP_SETUP_EXPIRED绑定会话过期
4006306PASSWORD_LOGIN_DISABLED站点强制 SSO,密码登录已禁用

5300、5400、5312 等安全类错误都使用 HTTP 400(只有模型配额 6103 使用 429)。请以响应体中的 code 为准,详见 API 错误与重试。

相关文档

  • TOTP API:两步验证的绑定、备用码与状态查询
  • SSO API:身份提供商配置、登录跳转与回调
  • Users API:修改密码、密码状态查询与账号注销
  • API 快速开始:用 API Key 直接调用 Agent

这篇文章对你有帮助吗?

本页目录