Skip to content
bitzorcas
中EN

Reference

Identity 前端

BitzOrcas Web Identity 完整使用与开发参考:登录(RSA 密码密文)、验证码、MFA、密码、邀请准入、用户授权、组织、应用凭据与租户治理。

Last updated

Identity 是 BitzOrcas Platform 第一条完整落地的前端纵切片。它不是一张孤立登录页, 而是从公开入口、认证状态、账号生命周期到管理端安全操作的一套信任边界。页面统一消费 Design System 1.2 与 @bitz/platform-sdk,不保存 refresh token,不手写后端 DTO, 也不在页面内直接 fetch。

1. 页面入口

公开入口

路由用途
/login凭证登录;按服务端响应进入验证码、MFA 或强制改密
/forgot-password请求密码重置;始终显示一致结果,避免账号枚举
/reset-password?userId=&token=从安全链接设置新密码
/accept-invitation?token=接受定向/开放邀请并提交准入资料
/activate?token=审批通过后激活账号并设置首次密码
/verify-email?userId=&token=确认邮箱
/verify-phone?userId=&token=确认手机号
/external-login/complete外部登录 callback 后恢复 HttpOnly Cookie 会话
/session-expired会话失效后的明确恢复入口

认证入口

路由用途
/change-password主动改密或完成强制改密
/settings/securityMFA、恢复码、通行密钥、OTP、设备、会话与个人登录历史
/users用户搜索、创建、编辑、状态、组织和角色
/identity/access邀请、准入、角色权限、ABAC、功能开关与租户登录审计
/identity/organization组织树与层级维护
/identity/applicationsAPI Key、HMAC 客户端与 OAuth 应用
/host/tenants租户创建、供给、暂停、恢复与停用(Host 面)
/integrations外部登录、SCIM、Webhooks、通知与 AI 等租户集成

2. 登录状态机

captcha换一张mfapassword-change-requiredauthenticated

账号 + 密码

GET /api/auth/cipher-key

Web Crypto RSA-OAEP 加密

POST /api/auth/login 密文

LoginOutcome

显示服务端图形验证码

POST /api/auth/captcha/refresh

TOTP / 恢复码 / 通行密钥

POST /api/mfa/verify 或 FIDO2

/change-password

GET /api/auth/me

返回原目标路由

页面不根据空 token 或 HTTP 状态猜分支。AuthSession.handleLoginSuccess() 把服务端响应 收敛为稳定的联合类型:

// 用可辨识联合约束登录后续步骤,页面不解析临时字段猜测状态。
type LoginOutcome =
| { kind: "authenticated" }
| { kind: "captcha"; challengeId: string; renderData: string }
| { kind: "mfa"; mfaToken: string }
| { kind: "mfa-enrollment-required" }
| { kind: "password-change-required" };

图形验证码不是每次登录的固定步骤。只有连续失败,或系统检测到异常网络、设备等风险时, 服务端才会要求追加验证。页面显示“换一张”按钮;点击后只提交账号和上一张验证码的标识, 不会再次发送密码。刷新后旧验证码立即失效;验证码错误或本次登录失败时,页面也会自动 换一张、清空输入并把焦点放回验证码输入框。

验证码支持 SVG、PNG、JPEG、GIF Base64 与 data/blob URL。MFA 验证使用短时 MfaToken 证明已经完成密码阶段,不发送 Bearer token。

登录页动态效果

登录页沿用法律顾问工作室 C4D 视觉。静态海报始终作为首屏和加载失败时的基线;只有桌面端、 未开启“减少动态效果”且未启用省流量模式时才加载静音循环视频。小屏和上述偏好场景只显示 静态海报,功能与信息不依赖动画。视频不进入 PWA 预缓存,避免首次使用额外下载大体积素材。

外观设置

公开 Identity 页面右上角提供“外观设置”,可切换浅色、深色、跟随系统以及衡蓝、香槟金、 章红、青律。登录、验证码、MFA、邀请和找回密码固定使用 Comfortable 密度,因此不显示 无效的密度选项;登录后的全局顶栏另提供 Dense、Compact、Comfortable 三档。

Mode、Brand 和 Density 只改变视觉表现,不改变路由、登录分支、权限或 SDK。页面只在 localStorage 保存这三项非敏感视觉偏好;账号、工作空间、权限和凭据仍全部来自服务端。 浏览器拒绝持久化时切换仍即时生效,但只保留到当前页面会话。

3. 会话与凭据

  • access token 只存在 AuthSession 的内存字段。
  • refresh token 只存在后端设置的 HttpOnly Cookie。
  • 首屏先 refresh,再读取 /api/auth/me;完成前状态为 bootstrapping。
  • 并发 401 共用一个 refresh Promise,成功后每个原请求只重试一次。
  • sessionRevision 阻止旧 refresh 覆盖稍后发生的登录、MFA 或登出。
  • logout 请求先尝试撤销服务端会话;网络失败也会清理本地内存状态。

一次性链接的 token 和 userId 会在启动阶段捕获,随后使用 replace 导航从地址栏清除, 避免被浏览器历史、资源 Referer 或截图继续携带。

4. MFA 与安全中心

两阶段启用

  1. POST /api/mfa/setup 返回二维码和手工密钥,只保存短时 pending secret。
  2. 用户使用 Authenticator 扫码并输入首个 6 位 TOTP。
  3. POST /api/mfa/setup/confirm 验证成功后才正式启用 MFA。
  4. 禁用 MFA 必须再次提供当前 TOTP。

恢复码

  • 状态端点只返回剩余数量,不回传历史明文。
  • 重新生成必须通过当前 TOTP。
  • 新代码只在本次响应显示;页面提供保存提醒,不写入浏览器持久化存储。
  • 登录使用一组未消费的 16 位恢复码;成功后该码立即失效。

通行密钥

  • 安全中心通过 FIDO2 register begin/complete 登记系统钥匙串或安全密钥。
  • 登录阶段通过 login begin 取得 challenge,再调用 navigator.credentials.get()。
  • 浏览器不支持或用户取消时明确降级到 TOTP/恢复码。
  • 生物特征数据不会发送给应用;服务端只验证公钥凭据、challenge、RP 和 origin。

可信设备、会话与审计

  • 信任当前设备需要 TOTP,服务端按设备归属记录 30 天有效期。
  • 可以撤销设备信任、移除设备、撤销单个会话或其他全部会话。
  • 个人登录历史展示结果、验证方式、IP、设备和失败原因。
  • 租户管理员可在“准入与授权”按用户查询租户登录审计。

5. 密码与联系方式

密码页面读取 GET /api/password/policy 返回的部署实际策略:

// 长度与唯一字符数直接取自服务端密码策略。
type PasswordPolicyResponse = {
requiredLength: number;
maximumLength: number;
requiredUniqueChars: number;
requireDigit: boolean;
requireLowercase: boolean;
// 三个字符类别开关分别驱动界面提示与本地预检。
requireUppercase: boolean;
requireNonAlphanumeric: boolean;
};

激活、重置和改密共用同一个策略 hook 与规则组件。策略端点暂时不可达时使用保守 fallback, 但服务端始终执行最终校验;密码历史、当前密码和租户策略不能由浏览器替代。

通知行为:

  • 密码重置申请始终返回一致结果,避免根据响应枚举账号。
  • 邮箱验证发送一次性链接。
  • 手机确认和 SMS OTP 发送 6 位验证码。
  • 未配置邮件/短信供应商时服务端会明确失败;“API 成功”不能代替真实消息可达性验收。

6. 邀请、准入与用户

/identity/access 区分邀请、准入、授权策略和登录审计:

  • 邀请支持定向邮箱和开放链接,可配置允许域、角色、组织、办公室、到期时间与使用次数。
  • 接受邀请只创建 PendingReview 准入申请,不直接创建可登录用户。
  • 审核通过后创建待激活用户并发送激活指引。
  • 拒绝、撤销与其他危险动作都经过二次确认。

/users 提供用户搜索、服务端分页、创建、资料编辑、组织归属、角色分配、锁定、解锁、 启用、禁用和删除。资料更新与角色授权使用不同接口和权限边界,避免把授权变更夹在普通 资料表单里。

7. 角色、ABAC 与功能开关

  • 角色负责汇集权限;用户只分配角色,不在用户表单中拼装零散权限。
  • 权限树和角色授权来自服务端。
  • ABAC 规则以稳定枚举提交作用域、动作、条件、裁决和优先级。
  • Feature 管理同时展示平台默认、租户覆盖与实际生效状态。
  • 前端 gate 只改善可见性和交互;后端 Authorization 是唯一安全事实来源。

8. 组织、应用凭据与租户

组织架构支持根/子组织创建、编辑、移动和删除。页面会排除明显非法目标,服务端最终防止 移动到自身或后代,并保护存在成员/子组织时的完整性。

应用与凭据页统一管理:

  • API Key:最小 scope、轮换与撤销;
  • HMAC:请求签名客户端、scope、secret 轮换;
  • OAuth 应用:redirect URI、scope、PKCE 与 client secret。

所有 secret 只在创建或轮换响应中显示一次,列表永远不回显明文。

租户治理按后端状态机提供创建、供给步骤、暂停、恢复和停用。页面不自行推测非法转换, 每次操作都带理由并等待服务端裁决。

/integrations 的外部登录 Sheet 管理当前租户的 Azure AD、通用 OAuth2、LDAP 与微信小程序 配置。列表只显示已配置的 Secret 键名,不回显凭据;保存支持替换或明确清除 Secret,删除带 Version 乐观并发,连接测试使用已保存配置。该入口要求 identity.externallogin.manage, 协议 Adapter 仍由 Host 静态装载。字段、就绪状态和运行时失效规则见 Identity 配置与外部集成。

9. 页面开发约束

页面只消费统一 API
// 统一 API 结果先映射稳定错误,再更新会话列表状态。
const { api } = usePlatform();
const result = await api.listSessions();
// 失败分支不把后端内部信息直接呈现给用户。
if (!result.ok) {
setMessage(friendlyIdentityError(result.error, t.tRaw));
return;
}
setSessions(result.data);
  • HTTP 只走 usePlatform().api;登录协议走 Provider 的 login / verifyMfa / logout。
  • DTO 从 @bitz/platform-sdk 导入;字段来自 OpenAPI generated client。
  • 原语从 @bitz/components,复合件从 @bitz/widgets;禁止深路径导入。
  • 表单字段使用 IdentityField 连接 label、hint、error 和 ARIA 属性。
  • 危险操作使用 ConfirmAction;提交中禁用重复动作并显示 Spinner。
  • 颜色、字体、间距、圆角、阴影和导航前景全部消费 Design System 1.2 Token。

10. 部署验收

  1. 配置有效 Runtime License,并以真实 Api Host 启动应用。
  2. Frontend:BaseUrl 使用公开 HTTPS origin。
  3. 邮件、短信与外部身份 Provider 已配置真实适配器;集成中心显示 Ready,并通过真实连接测试。
  4. refresh Cookie 的 Secure、SameSite、Domain 与部署拓扑一致。
  5. 用真实 Authenticator 完成 setup → confirm → logout → MFA login。
  6. 用平台钥匙串或安全密钥完成 passkey register → login → delete。
  7. 验证恢复码只能消费一次,可信设备撤销后恢复完整 MFA。
  8. 用最低权限账号复核页面、页签、按钮和服务端 403。
  9. 验证用户、角色、设备、会话、组织、审计和凭据不能跨租户读取。

11. 回归命令

Terminal window
# 后端契约与应用测试
scripts/build/export-openapi.sh
dotnet test tests/BitzOrcas.Application.Tests/BitzOrcas.Application.Tests.csproj \
--filter 'FullyQualifiedName~Identity|FullyQualifiedName~Authorization'
# 前端
cd frontend
yarn workspace @bitz/platform-sdk generate-client
yarn workspace @bitz/platform-sdk test
yarn workspace @bitz/materials test
yarn lint
yarn typecheck
yarn build
# Identity 设计防腐:三个命令均应无输出
rg -n '#[0-9A-Fa-f]{3,8}|rgb\\(|hsl\\(|oklch\\(' \
apps/app/src/pages/identity apps/app/src/pages/login
rg -n -- '--ref-|--login-' apps/app/src/pages/identity apps/app/src/pages/login
rg -n '\\b(fetch|axios|XMLHttpRequest)\\b|localStorage|sessionStorage' \
apps/app/src/pages/identity apps/app/src/pages/login

返回前端 · 平台 SDK · Design System 1.2

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%