架构总览
@tripo3d/auth 是 Tripo 在 Ory Kratos 上封装的认证客户端,把 Kratos 复杂的多步 Flow + Identity API 收敛成几条业务方法。它处理:
- Flow 管理 — login / signup / verification / recovery / settings / logout 各自有"创建 → 提交"两步
- JWT 缓存与续期 — 60 秒前主动刷新,前端无感
- OIDC 弹窗通信 — BroadcastChannel + postMessage 双通道,跨域 / 跨标签页可靠
- Continue-flow — 第三方账号缺邮箱时的补充注册流程
- 错误归一化 — 把 Kratos 多种错误形态统一包装为
TripoAuthError
一图概览
TripoAuth入口类,持有配置 + JWT 缓存 + Ory FrontendApi 实例createLoginFlow / createSignupFlow密码 / 验证码 / OIDC 三种登录入口createVerificationFlow邮箱 / 手机号验证(已登录态)createRecoveryFlow密码找回(发码 → 验证 → 自动登录到验证态)createSettingFlow改密、修改 traits(需已登录或 recovery 验证态)createLogoutFlow清除 Ory 服务端 sessioncreateMergeFlowOIDC 账号合并(已有 flowId)
JWT 服务initTokenized / setTokenized / tokenized()内存缓存_tokenized + _exp,跨方法共享过期检测jwtDecode 解析 exp,now + 60s < exp 时复用,否则重取
工具方法辅助 APIcheckIdentifier查询 email/phone 是否已注册,返回 flowType=login|registrationgetLoginFlowMessages / Outcome从已存在的 flow id 提取 UI 消息或 OIDC 取消状态sendVerificationCode / verifyEmailCode422 continue-flow 自定义验证(走业务后端,非 Kratos)
OIDC 回调弹窗内执行,主窗口通过 BroadcastChannel 监听oidcCallback正常回调,读取 URL 参数广播continueFlowCallback422 时把 flow id 广播给主窗口oidcCancelledCallback / oidcErrorCallback用户取消 / 第三方报错
五层抽象
1. Configuration(连接配置)
构造时传入 basePath(Ory 服务地址)、可选的 headers / fetchApi / allowedOrigins / continueFlowProxyBasePath / middleware / fetchTokenized。SDK 会:
- 通过统一的
RequestClient.fetcher自动注入默认值,所有通过 SDK 发出的请求(Ory_api/ 5 个自定义端点 / 业务方传入的fetchTokenized)共享同一份默认值与 middleware 链:credentials: 'include'(带 Cookie)Accept: application/jsonContent-Type: application/json—— 仅在 body 是字符串(JSON.stringify(...))且未显式设置时注入;FormData / Blob / URLSearchParams 不会被覆盖
- 上面三项都仅在调用方未显式设置时才补,业务方传
credentials: 'omit'或自定义 Content-Type 仍生效 - 实例化
@ory/client-fetch的FrontendApi - 不会自动检查登录,需要手动调
initTokenized()
2. Flow(流程)
Kratos 的每条业务路径都是一个 Flow——服务端创建一个带 id 与 csrf_token 的临时上下文,客户端在其上做 N 步提交。SDK 把这一对"创建 + 操作"包成单个工厂:
const loginFlow = await auth.createLoginFlow();
await loginFlow.password({ email: 'a@b.c', password: '...' });
// └─ 内部 POST /self-service/login/flows/{flowId}每个 flow 工厂返回的对象都是闭包,持有内部 state.flow / state.csrf_token,失败时自动用响应里的最新 flow 状态更新自己,无需手动重建 flow。
3. Identity & Identifier
- Email / Phone 二选一:几乎所有 send/code/password 方法都接受
{ email }或{ phone },不做隐式判断 checkIdentifier(value):可在前端先查后端确定走 login 还是 registration,避免输完密码后才发现是新用户
4. JWT 与 Session
- Ory 用 Cookie 维护 session(
ory_kratos_session) - SDK 通过
toSession({ tokenizeAs: 'default_jwt' })把 session 换成 JWT,缓存到内存 tokenized()会在过期前 60 秒主动刷新,业务侧拿到的永远是有效 token- 不持久化到 localStorage—需要跨刷新保留请自行存储 +
setTokenized()复原
5. Error 归一化
所有失败路径都抛 TripoAuthError,带 name 字段标识来源:
| error.name | 触发条件 | 说明 |
|---|---|---|
Flow | response.ui.messages 或 ui.nodes[].messages 含 type=error | Kratos 表单校验或业务规则错误,例如密码弱、验证码错 |
Response | response.error 字段存在 | Kratos HTTP 层错误(4xx / 5xx 携带 code+message) |
Unknown | fetch 异常或非预期状态 | 网络错误或未预期的响应形态 |
OIDC | OIDC 弹窗回传 error 消息 | 第三方授权失败 / 用户拒绝(access_denied) |
CheckIdentifier | checkIdentifier 接口失败 | 识别 email/phone 是否存在的查询失败 |
SendVerificationCode / VerifyEmailCode | 422 continue-flow 业务接口失败 | 走业务后端的 send/verify 流程异常 |
详见 错误处理。
OIDC 弹窗的双通道协议
OIDC 登录会弹出新窗口跳到第三方,完成后第三方重定向回 /auth/oidc,弹窗里执行 oidcCallback(),把结果广播回主窗口。SDK 同时使用 BroadcastChannel 与 postMessage 两条通道,以适配:
| 场景 | 通道 |
|---|---|
| 同源 + 同浏览器上下文 | BroadcastChannel(更可靠,跨标签页) |
| 跨域 / 浏览器对 BC 限制 | postMessage(必须配置 allowedOrigins 校验来源) |
完整时序与代码示例见 OIDC 登录。
帧外的几个关键概念
transient_payload
所有方法的可选第二参数。Kratos 把它转发到 webhook / 自定义钩子,不写入 identity traits。常用来上送埋点 / 来源 / 用户偏好等会话级元数据。
Continue-flow(422)
场景:用户用 Google 登录,但 Google 没暴露邮箱给我们。Ory 返回 422,要求补充邮箱。SDK 把这一过程拆成:
- OIDC popup 抛
{ code: '1001', data: { id: flowId } } - 主窗口拿 flowId,调
sendVerificationCode(email, provider)→ 业务后端发码 - 用户输入验证码,调
verifyEmailCode(email, code) - 调
getContinueFlow(flowId)拿到表单actionURL 与csrf_token - 用隐藏 form POST 到
action,Ory 完成注册
详见 OIDC 登录。
allowedOrigins 安全模型
OIDC postMessage 需要校验消息来源:
- 设
allowedOrigins: [window.location.origin]是最常见的安全配置 - 空数组
[]= 接受任何来源(等同*),仅本地开发使用 - 不设置 =
[]行为
快速开始
import { TripoAuth } from '@tripo3d/auth';
// 1. 实例化
const auth = new TripoAuth({
basePath: 'https://auth.tripo3d.ai',
allowedOrigins: [window.location.origin],
});
// 2. 检查现有 session
const tokenized = await auth.initTokenized();
if (tokenized) {
console.log('已登录,JWT:', tokenized);
}
else {
// 3. 走任意一种登录流程
const loginFlow = await auth.createLoginFlow();
await loginFlow.password({ email: 'a@b.c', password: '••••••' });
// 之后任何时刻拿 token:
const t = await auth.tokenized();
}下一步
- 密码流程 — 注册 / 登录 / 改密
- 验证码流程 — Email / Phone OTP
- OIDC 登录 — Google / Apple / Continue-flow
- 账号找回 — 忘记密码与邮箱/手机验证
- 错误处理 — TripoAuthError 与错误码
- API Reference — 自动生成的完整签名
- Playground — 可切换环境的真实接口测试