Skip to content

架构总览

@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 服务端 session
    • createMergeFlowOIDC 账号合并(已有 flowId)
  • JWT 服务initTokenized / setTokenized / tokenized()
    • 内存缓存_tokenized + _exp,跨方法共享
    • 过期检测jwtDecode 解析 exp,now + 60s < exp 时复用,否则重取
  • 工具方法辅助 API
    • checkIdentifier查询 email/phone 是否已注册,返回 flowType=login|registration
    • getLoginFlowMessages / 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/json
    • Content-Type: application/json —— 仅在 body 是字符串(JSON.stringify(...))且未显式设置时注入;FormData / Blob / URLSearchParams 不会被覆盖
  • 上面三项都仅在调用方未显式设置时才补,业务方传 credentials: 'omit' 或自定义 Content-Type 仍生效
  • 实例化 @ory/client-fetchFrontendApi
  • 不会自动检查登录,需要手动调 initTokenized()

2. Flow(流程)

Kratos 的每条业务路径都是一个 Flow——服务端创建一个带 idcsrf_token 的临时上下文,客户端在其上做 N 步提交。SDK 把这一对"创建 + 操作"包成单个工厂:

ts
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触发条件说明
Flowresponse.ui.messages 或 ui.nodes[].messages 含 type=errorKratos 表单校验或业务规则错误,例如密码弱、验证码错
Responseresponse.error 字段存在Kratos HTTP 层错误(4xx / 5xx 携带 code+message)
Unknownfetch 异常或非预期状态网络错误或未预期的响应形态
OIDCOIDC 弹窗回传 error 消息第三方授权失败 / 用户拒绝(access_denied)
CheckIdentifiercheckIdentifier 接口失败识别 email/phone 是否存在的查询失败
SendVerificationCode / VerifyEmailCode422 continue-flow 业务接口失败走业务后端的 send/verify 流程异常

详见 错误处理

OIDC 弹窗的双通道协议

OIDC 登录会弹出新窗口跳到第三方,完成后第三方重定向回 /auth/oidc,弹窗里执行 oidcCallback(),把结果广播回主窗口。SDK 同时使用 BroadcastChannelpostMessage 两条通道,以适配:

场景通道
同源 + 同浏览器上下文BroadcastChannel(更可靠,跨标签页)
跨域 / 浏览器对 BC 限制postMessage(必须配置 allowedOrigins 校验来源)

完整时序与代码示例见 OIDC 登录

帧外的几个关键概念

transient_payload

所有方法的可选第二参数。Kratos 把它转发到 webhook / 自定义钩子,不写入 identity traits。常用来上送埋点 / 来源 / 用户偏好等会话级元数据。

Continue-flow(422)

场景:用户用 Google 登录,但 Google 没暴露邮箱给我们。Ory 返回 422,要求补充邮箱。SDK 把这一过程拆成:

  1. OIDC popup 抛 { code: '1001', data: { id: flowId } }
  2. 主窗口拿 flowId,调 sendVerificationCode(email, provider) → 业务后端发码
  3. 用户输入验证码,调 verifyEmailCode(email, code)
  4. getContinueFlow(flowId) 拿到表单 action URL 与 csrf_token
  5. 用隐藏 form POST 到 action,Ory 完成注册

详见 OIDC 登录

allowedOrigins 安全模型

OIDC postMessage 需要校验消息来源:

  • allowedOrigins: [window.location.origin] 是最常见的安全配置
  • 空数组 [] = 接受任何来源(等同 *),仅本地开发使用
  • 不设置 = [] 行为

快速开始

ts
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();
}

下一步

基于 MIT 协议发布(内部使用)