Skip to content

密码流程

@tripo3d/auth 把"密码"作为一种 method 复用在三条 flow 上:登录、注册、改密。三者共用同一组 password() 子方法,差别仅在调用前用哪个工厂创建 flow:

  • createLoginFlow().password() — 已存在用户登录
  • createSignupFlow().password() — 新用户注册并自动登录
  • createSettingFlow().password() — 已登录态(或 recovery 验证态)修改密码

底层都是 Kratos POST /self-service/{login|registration|settings}/flows/{flowId} ,SDK 替你处理 csrf_token、错误归一化与 JWT 缓存。

登录(已存在用户)

最常见的入口。需要先 createLoginFlow() 拿到一个 flow 上下文,再调 password({ email | phone, password }):

ts
import { TripoAuth } from '@tripo3d/auth';

const auth = new TripoAuth({ basePath: 'https://auth.tripo3d.ai' });

try {
  const loginFlow = await auth.createLoginFlow();
  await loginFlow.password({
    email: 'a@b.c',
    password: '••••••',
  });
  // 此时 SDK 已自动调用 initTokenized(),JWT 已写入内存缓存
  const token = await auth.tokenized();
}
catch (error) {
  const e = error as Error;
  if (e.name === 'Flow') {
    // 表单错误:identifier 不存在 / 密码错误 / 账号锁定等
    console.warn('登录失败:', e.message);
  }
}

identifier 由调用方显式给出(emailphone),SDK 不做隐式判断。手机号同理:

ts
await loginFlow.password({ phone: '+8613800138000', password: '••••••' });

请求形态

调用 loginFlow.password() 后,实际发出的请求大致如下:

http
POST /self-service/login/flows?flow={flowId}
Content-Type: application/json
Cookie: ory_kratos_session=...

{
  "csrf_token": "...",
  "method": "password",
  "identifier": "a@b.c",
  "password": "••••••",
  "transient_payload": null
}

成功响应携带 session ,Cookie 由浏览器自动写入,SDK 紧接着调 toSession({ tokenizeAs: 'default_jwt' }) 拿 JWT。

错误形态

handleFlow 会把响应里的错误归一化成 TripoAuthError,带 name 区分来源:

error.name来自说明
Flowresponse.ui.messages 或 ui.nodes[].messages 中 type=error 的第一条密码错误、账号被锁、identifier 不存在(后端策略)等表单语义错误
Responseresponse.error.messageKratos HTTP 层错误(如 410 flow 过期、429 频控)
Unknown请求异常或非 JSON 响应网络断、CORS 失败等

flow 状态会在响应回来时就地更新——同一个 loginFlow 对象可以直接重试:

ts
try {
  await loginFlow.password({ email, password: wrong });
}
catch {
  // flowId 与 csrf_token 已经被替换为响应里的最新值,直接再调即可
  await loginFlow.password({ email, password: correct });
}

注册(新用户)

createSignupFlow().password() 的签名与登录一致,但语义不同:Kratos 会创建一个新的 identity 并自动建立 session,所以同样会触发 initTokenized()

ts
const signupFlow = await auth.createSignupFlow();
await signupFlow.password({
  email: 'newuser@b.c',
  password: '••••••',
});
// 注册即登录,token 已可用
const token = await auth.tokenized();

与登录的差异

名称 类型 默认值 说明
identity新建 / 已存在注册要求 identifier 在租户内不存在,否则 Flow 错误返回「该邮箱已注册」
traits注册写入注册请求体里携带 traits: { email } 或 { phone },直接写入 identity 的 traits
session自动建立注册成功 = 登录成功,无需额外调 createLoginFlow

手机号注册的特殊性

虽然 signupFlow.password() 的 TS 签名允许 { phone, password },但实际能否成功取决于 Kratos schema:Tripo 后端手机号必须先发码验证才能完成注册,直接走 password 通常会被拒。手机号注册推荐使用验证码流程:

ts
// 推荐写法:phone 走 code,而不是 password
const signupFlow = await auth.createSignupFlow();
await signupFlow.send({ phone: '+8613800138000' });
await signupFlow.code({ phone: '+8613800138000', code: '123456' });

改密(已登录态)

修改密码走 settings flow,前置条件是已存在 session。两种典型来源:

  • 用户已登录(tokenized() 能拿到 token)
  • 通过 createRecoveryFlow().verify(code) 拿到的 recovery 验证态(详见 账号找回)
ts
// 1. 确保有 session
const token = await auth.tokenized();
if (!token) throw new Error('未登录,请先登录');

// 2. 创建 settings flow 并提交新密码
const settingFlow = await auth.createSettingFlow();
await settingFlow.password('NewP@ssw0rd2026');

createSettingFlow() 当前只暴露 password 子方法,改邮箱/手机号需走 verification 流程或后端单独 API。

Recovery 联动

recovery flow verify(code) 成功后,SDK 会自动 initTokenized(),这时虽然没有「常规登录」,但 Ory 会发放一个有限制的 session(privileged session),允许调用 createSettingFlow().password() 改密:

ts
const recoveryFlow = await auth.createRecoveryFlow();
await recoveryFlow.send({ email: 'forgot@b.c' });
await recoveryFlow.verify('123456');
// ↑ 进入 recovery 验证态

const settingFlow = await auth.createSettingFlow();
await settingFlow.password('NewP@ssw0rd');
// ↑ Kratos 接受改密,但通常拒绝改 traits

完整流程见 账号找回

API 参考

方法签名说明
createLoginFlow(returnTo?: string) => Promise<LoginFlowHandle>创建登录 flow,可选传入 returnTo 用于 OIDC 跳回
createSignupFlow() => Promise<SignupFlowHandle>创建注册 flow
createSettingFlow() => Promise<SettingFlowHandle>创建改密 / 设置 flow,需要已存在 session
loginFlow.password(params, transient_payload?) => Promise<void>params 为 { email, password } 或 { phone, password };成功后自动 initTokenized()
signupFlow.password(params, transient_payload?) => Promise<void>同上,但创建新 identity;成功后自动登录
settingFlow.password(password: string, transient_payload?) => Promise<void>直接传新密码字符串;无返回值,失败抛错

params 详细字段

名称 类型 默认值 说明
emailstring邮箱地址,与 phone 二选一
phonestringE.164 格式手机号(如「+8613800138000」),与 email 二选一
password必填string明文密码,Kratos 后端校验强度
transient_payloadRecord<string, any>可选,转发到 Kratos webhook,常用于埋点 / 来源记录;不写入 traits

常见错误

弱密码

Kratos 默认开启 haveibeenpwned + 长度校验,弱密码会以 name='Flow' 抛出:

ts
try {
  await signupFlow.password({ email, password: '123456' });
}
catch (error) {
  const e = error as Error;
  // e.message 形如「The password must be longer than 8 characters」
  // 或「This password has been found in data breaches and must not be used」
  showToast(e.message);
}

identifier 已存在(注册场景)

注册时如果邮箱已注册,Kratos 会返回 Flow 错误。建议在调 password() 前先用 checkIdentifier() 判断:

ts
const { flowType } = await auth.checkIdentifier('a@b.c');
if (flowType === 'login') {
  // 已注册,引导到登录页
}
else {
  const signupFlow = await auth.createSignupFlow();
  await signupFlow.password({ email: 'a@b.c', password: '••••••' });
}

Session 过期(改密场景)

settings flow 要求 session 处于 privileged 状态(通常需要近期登录过)。过期时 Kratos 会要求重新认证,SDK 抛出 Response 错误:

ts
try {
  const settingFlow = await auth.createSettingFlow();
  await settingFlow.password('NewP@ssw0rd');
}
catch (error) {
  const e = error as Error;
  if (e.name === 'Response' && e.message.includes('privileged')) {
    // 引导用户重新登录后再来改密
    redirect('/login?next=/settings/password');
  }
}

下一步

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