Files
story/openapi/operator-login.yaml
T

311 lines
14 KiB
YAML
Raw Normal View History

openapi: 3.1.0
info:
title: 管理端手机号登录
version: 1.1.0
paths:
/api/v1/web/operator/login/session:
get:
operationId: operatorLoginSession
summary: 初始化手机号登录页面
description: HTTPS,同源,不接受查询参数。签发 HttpOnly 登录 Cookie 和 CSRF token,20 分钟后过期,同一 Cookie 重载不会轮换 CSRF。短信不可用时仍可使用密码登录。
security: []
responses:
'200':
description: 页面状态;sms_enabled=false 不代表发送成功
headers:
Set-Cookie:
schema: { type: string }
content:
application/json:
schema:
type: object
required: [ok, data]
properties:
ok: { type: boolean }
data:
type: object
required: [sms_enabled, password_enabled, csrf_token]
properties:
sms_enabled: { type: boolean }
password_enabled: { type: boolean }
signup_enabled: { type: boolean, const: false }
csrf_token: { type: string }
default: { $ref: '#/components/responses/Failure' }
/api/v1/web/operator/login/sms/send:
post:
operationId: operatorSendLoginSMS
summary: 发送管理端登录验证码
description: 6 位随机验证码,5 分钟有效;每个手机号至少间隔 60 秒、每小时最多 5 次,家庭端和设备绑定入口共用此限额。同一请求号重试不重复发送。手机号加密、验证码仅存 keyed hash;不在响应中返回验证码。
security: [{ OperatorLoginCookie: [] }]
parameters:
- { $ref: '#/components/parameters/RequestID' }
- { $ref: '#/components/parameters/CSRF' }
requestBody:
required: true
content:
application/json:
schema: { $ref: './onboarding.yaml#/components/schemas/SMSSendInput' }
responses:
'200':
description: 网关已接受发送请求
content:
application/json:
schema: { $ref: './onboarding.yaml#/components/schemas/SMSResponse' }
default: { $ref: '#/components/responses/Failure' }
/api/v1/web/operator/login/sms/verify:
post:
operationId: operatorVerifyLoginSMS
summary: 验证验证码并创建管理端登录会话
description: 验证码只能在发起发送的浏览器会话使用,最多尝试 5 次,成功后一次性消费。已有短信账号复用原身份;管理端不开放自助注册,必须预先绑定已有有效管理身份。账号 Cookie 24 小时有效,调用家庭写接口还需 CSRF;保留原管理角色,写操作仍按现有角色权限校验。读取 /api/v1/me 或事件流需附带 X-Login-Portal=operator。
security: [{ OperatorLoginCookie: [] }]
parameters:
- { $ref: '#/components/parameters/RequestID' }
- { $ref: '#/components/parameters/CSRF' }
requestBody:
required: true
content:
application/json:
schema: { $ref: './onboarding.yaml#/components/schemas/SMSVerifyInput' }
responses:
'200':
description: 签发 HttpOnly 账号 Cookie,消费匿名登录会话;前端继续读取 /api/v1/me 校验业务账号
headers:
Set-Cookie:
schema: { type: string }
content:
application/json:
schema:
type: object
required: [ok, data]
properties:
ok: { type: boolean }
data:
type: object
required: [csrf_token, data]
properties:
csrf_token: { type: string }
data:
type: object
required: [authenticated]
properties:
authenticated: { type: boolean }
default: { $ref: '#/components/responses/Failure' }
/api/v1/web/operator/login/captcha:
post:
operationId: operatorLoginCaptcha
summary: 获取或更换密码登录、发送短信前的图片验证码
description: 返回 PNG 图片和随机 ID,不返回答案。绑定登录 Cookie,2 分钟有效;刷新使旧图失效;每次校验均消费验证码。校验状态保存在 Redis,依赖不可用时拒绝登录。
security: [{ OperatorLoginCookie: [] }]
parameters:
- { $ref: '#/components/parameters/RequestID' }
- { $ref: '#/components/parameters/CSRF' }
requestBody:
required: true
content:
application/json:
schema: { type: object, additionalProperties: false }
responses:
'200':
description: 图片验证码
content:
application/json:
schema:
type: object
required: [ok, data]
properties:
ok: { type: boolean }
data:
type: object
required: [captcha_id, mode, expires_at_ms]
properties:
captcha_id: { type: string, format: uuid }
mode: { type: string, enum: [image, tencent, slide] }
captcha_app_id: { type: string, description: 腾讯云模式对应客户端的公开 CaptchaAppId,不含密钥 }
image: { type: string, pattern: '^data:image/(png|jpeg);base64,' }
thumb_image: { type: string, pattern: '^data:image/png;base64,' }
image_width: { type: integer, minimum: 1 }
image_height: { type: integer, minimum: 1 }
thumb_width: { type: integer, minimum: 1 }
thumb_height: { type: integer, minimum: 1 }
thumb_x: { type: integer, minimum: 0, description: 拼图块的初始展示横坐标 }
thumb_y: { type: integer, minimum: 0, description: 拼图块所在水平轨道 }
expires_at_ms: { type: integer, format: int64 }
default: { $ref: '#/components/responses/Failure' }
/api/v1/web/operator/login/captcha/verify:
post:
operationId: operatorVerifyLocalSlideCaptcha
summary: 校验本地滑块位置并换取一次性票据
description: 同源、CSRF 和运营登录会话校验;挑战每次提交即消费。成功票据绑定原会话、客户端、策略版本和原到期时间,只能用于一次短信发送或密码登录。
security: [{ OperatorLoginCookie: [] }]
parameters:
- { $ref: '#/components/parameters/RequestID' }
- { $ref: '#/components/parameters/CSRF' }
requestBody:
required: true
content:
application/json:
schema: { $ref: './login-captcha.yaml#/components/schemas/SlideCaptchaVerifyInput' }
responses:
'200':
description: 位置正确,返回一次性校验票据
content:
application/json:
schema: { $ref: './login-captcha.yaml#/components/schemas/SlideCaptchaVerifyResponse' }
default: { $ref: '#/components/responses/Failure' }
/api/v1/web/operator/login/password:
post:
operationId: operatorLoginPassword
summary: 手机号、密码和图片验证码登录
description: 使用与短信登录一致的手机号身份,密码登录不自动注册。每手机号 10 分钟最多 10 次密码尝试;每登录会话最多 20 次。失败返回统一的手机号或密码错误,不暴露手机号是否注册。密码正确但没有该账号的有效设备验证 Cookie 时返回 403 NEW_DEVICE_SMS_REQUIRED,不消费登录会话、不发账号 Cookie;完成现有 LOGIN 短信验证后签发 30 天 HttpOnly 设备验证 Cookie。已验证设备成功消费登录会话,签发 24 小时账号 Cookie。
security: [{ OperatorLoginCookie: [] }]
parameters:
- { $ref: '#/components/parameters/RequestID' }
- { $ref: '#/components/parameters/CSRF' }
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [phone_e164, password, captcha_id]
anyOf:
- required: [captcha_code]
- required: [captcha_ticket]
properties:
phone_e164: { type: string, pattern: '^\+[1-9][0-9]{7,14}$' }
password: { type: string, minLength: 1, maxLength: 256, writeOnly: true }
captcha_id: { type: string, format: uuid }
captcha_code: { type: string, pattern: '^[0-9]{6}$', writeOnly: true }
captcha_ticket: { type: string, maxLength: 4096, writeOnly: true }
captcha_randstr: { type: string, maxLength: 256, writeOnly: true, description: 腾讯云 Web 模式必填,本地滑块模式无需提供 }
responses:
'200':
$ref: '#/paths/~1api~1v1~1web~1operator~1login~1sms~1verify/post/responses/200'
default: { $ref: '#/components/responses/Failure' }
/api/v1/web/operator/account/password:
get:
operationId: operatorPasswordState
summary: 查看当前手机号和密码设置状态
security: [{ OperatorAccountCookie: [] }]
responses:
'200':
description: 仅返回脱敏手机号、是否已设置密码、是否具有最近 10 分钟内且未消费的短信验证,不返回密码或摘要
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
data:
type: object
required: [available]
properties:
available: { type: boolean }
phone_masked: { type: string }
password_configured: { type: boolean }
sms_verified_recently: { type: boolean }
default: { $ref: '#/components/responses/Failure' }
post:
operationId: operatorSetPassword
summary: 设置、修改或重置当前手机号的密码
description: 需要当前有效账号 Cookie、同源及 CSRF;同时需要最近 10 分钟内且未消费的短信验证,或正确的当前密码。新密码 6–64 个 Unicode 字符且不超过 256 字节。修改后清除短信证明并撤销该身份的其他管理登录会话,保留本次会话。密码使用随机盐 Argon2id 存储。
security: [{ OperatorAccountCookie: [] }]
parameters:
- { $ref: '#/components/parameters/RequestID' }
- { $ref: '#/components/parameters/CSRF' }
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required: [password]
properties:
password: { type: string, minLength: 6, maxLength: 64, writeOnly: true }
current_password: { type: string, maxLength: 256, writeOnly: true }
responses:
'200':
description: 密码已保存
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
data:
type: object
properties:
password_configured: { type: boolean }
default: { $ref: '#/components/responses/Failure' }
/api/v1/web/operator/account/session:
get:
operationId: operatorAccountSession
summary: 恢复管理端登录会话并获取 CSRF
security: [{ OperatorAccountCookie: [] }]
responses:
'200':
description: 有效管理账号会话
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
data:
type: object
required: [authenticated, csrf_token]
properties:
authenticated: { type: boolean }
csrf_token: { type: string }
default: { $ref: '#/components/responses/Failure' }
/api/v1/web/operator/auth/logout:
post:
operationId: operatorLogout
summary: 撤销当前管理端会话并清除其 Cookie
description: 同源与 CSRF 校验;家庭端会话不受影响。
security: [{ OperatorAccountCookie: [] }]
parameters:
- { $ref: '#/components/parameters/RequestID' }
- { $ref: '#/components/parameters/CSRF' }
requestBody:
required: true
content:
application/json:
schema: { type: object, additionalProperties: false }
responses:
'200':
description: 当前管理会话已退出
default: { $ref: '#/components/responses/Failure' }
components:
securitySchemes:
OperatorAccountCookie:
type: apiKey
in: cookie
name: __Host-platform-operator
description: 仅本地 HTTP 模式使用 platform-operator。与家庭端会话、密码独立;写操作需要同源及 CSRF。
OperatorLoginCookie:
type: apiKey
in: cookie
name: __Host-platform-operator-login
description: Secure、HttpOnly、SameSite=Lax;仅本地 HTTP 模式使用 platform-operator-login。
parameters:
RequestID:
in: header
name: X-Request-ID
required: true
schema: { type: string, format: uuid }
description: UUID v4,发送结果未知时用同一 ID 和正文重试。
CSRF:
in: header
name: X-CSRF-Token
required: true
schema: { type: string }
responses:
Failure:
description: 400/401/403/409/413/415/429/500/503;SMS_CODE_INVALID、LOGIN_SESSION_EXPIRED、SMS_RATE_LIMITED、SMS_SEND_UNCERTAIN、SMS_UNAVAILABLE、CAPTCHA_INVALID、CAPTCHA_UNAVAILABLE、PASSWORD_INVALID、PASSWORD_POLICY、CURRENT_PASSWORD_INVALID、SMS_REAUTH_REQUIRED、RATE_LIMITED 等,不返回验证码或密码。
content:
application/json:
schema: { $ref: './onboarding.yaml#/components/schemas/Failure' }