Auth
@tripo3d/auth· 基于 Ory Kratos 的统一认证 SDK。v2 把登录流程、UI 组件、跨端登录信号、token 缓存收敛成 6 个稳定的 namespace,浏览器端按需引入即用。
特性
Namespace 化 API
一个 TripoAuth 实例下挂 login / oidc / reset / settings / signal / token 六个 namespace + 顶层 logout,调用形态统一为 auth.{ns}.{verb}()。
自动分流登录
login.sendCode / submitCode 内部自动判定 login / registration,业务一套表单覆盖两种身份。
4 种 OIDC 模式
popup × dialog/standalone × pc/mobile 组合:startOidc({ mode }) 一键切换;mobile 自动降级 redirect。
Cookie 登录信号
跨标签 / 跨子域统一通过 __tripo_login_signal__ cookie 竞争认领;onLogin(handler) 注册实例回调,无 postMessage 跨域协议。
开箱即用 UI
子路径 /ui 暴露 LoginPage / OidcCompletePage / OidcEdgePage 三个页面组件 + 命令式入口 showLogin()(内部挂 LoginDialog,不直接对外);i18n 11 语言内置。
可定制 Token
默认走 ory toSession({ tokenizeAs: 'default_jwt' });业务方传 fetchToken 即可走自家 BFF 网关;过期前 60s 自动续期,logout 自动清缓存。
安装
请先完成 GitHub Packages 认证配置。
pnpm add @tripo3d/auth快速开始
import { TripoAuth } from '@tripo3d/auth';
const auth = new TripoAuth({
basePath: 'https://auth.tripo3d.ai',
onError: (err) => { /* 可选:监控 SDK 非阻塞运营错误 */ },
});
// 业务方注册当前实例的登录回调;登录信号通过 cookie 在同根域竞争认领
// SDK 在派发前已确保 token 就绪,handler 跑起来 token 直接可用
auth.signal.onLogin(async ({ signal, context }) => {
console.log('用户已登录', signal.provider, '冷启路径=', context.isRedirect);
await initUser();
});
// 退出登录订阅;auth.logout() 成功后自动派发(token 已 clear)
auth.signal.onLogout(() => clearLocalUserStore());
// 唤起登录对话框(来自 /ui 子路径);params 通过信号 cookie 透传给 onLogin handler
// showLogin 返回 void —— 登录完成统一靠上面的 onLogin handler 收尾
import { showLogin } from '@tripo3d/auth/ui';
showLogin(auth, {
completeUrl: 'https://www.tripo3d.ai/login/oidc', // 必填:OIDC 完成页绝对 URL
params: { fromBanner: 'home_v2' },
});
// 后续请求拦截器里按需读 token(默认实现 = 调 ory toSession,过期前 60s 自动续期)
const token = await auth.token.get();➡️ 更多接入细节看 5 分钟接入。
业务面一览
业务侧实际只面对 6 个 namespace + 1 个顶层方法。
class TripoAuth {
readonly login: LoginApi // sendCode / submitCode / submitPassword / startOidc
readonly signal: SignalApi // onLogin/onLogout / emitLogin/emitLogout / publishLogin/consumeSignal / readSignal/readSignalRecord/writeSignal/clearSignal
readonly oidc: OidcApi // handleComplete / loadEdgeFlow / promptGoogleOneTap / submitIdToken
readonly reset: ResetApi // 未登录恢复:sendCode / verifyCode / submitNewPassword
readonly settings: SettingsApi // 已登录改密码:sendVerifyCode / verifyCode / updatePassword
readonly token: TokenApi // peek / set / clear / get / refresh
logout(): Promise<void> // 失效 ory session + clearSignal + emitLogout(内部 token.clear)
}UI 部分作为独立子路径 @tripo3d/auth/ui 导出,业务方可选用:
import {
showLogin, // 命令式:挂载 LoginDialog 弹出登录
LoginPage, // 独立登录页(standalone 模式);:auth prop 传入 TripoAuth
OidcCompletePage, // OIDC 完成页;:auth prop 同上
OidcEdgePage, // OIDC 中间页(continue / merge);:auth prop 同上
DEFAULT_LOCALE, // 默认 locale 'en'
SUPPORTED_LOCALES, // 11 个 locale 列表
} from '@tripo3d/auth/ui';
import type { LocaleInput } from '@tripo3d/auth/ui';
// LocaleInput = LoginLocale | (string & {}) // showLogin({ locale }) 的参数类型;详见 UI 指南推荐接入模型
1. 命令式唤起登录对话框
// 在业务任意位置一键弹出登录 dialog;返回 void,登录完成走 onLogin 通知
// dialog 显示语言取 options.locale(宿主显式传入,缺省 'en'),params 透传到 onLogin handler
showLogin(auth, {
completeUrl: 'https://www.tripo3d.ai/login/oidc', // 必填:OIDC 完成页绝对 URL
locale: 'ja', // 可选:宿主当前 i18n locale;缺省回落 'en'
params: { fromBanner: 'home_v2' },
});2. 验证码登录(自动分流)
// SDK 内部 checkIdentifier 决定走 login 还是 registration
await auth.login.sendCode({ email: 'user@example.com' });
// submitCode 复用 sendCode 的活跃会话;transientPayload 透传给本次提交(与构造期 deviceId 合并,deviceId 优先)
await auth.login.submitCode(
{ code: '123456', email: 'user@example.com' },
{ transientPayload: { receiveMarketing: true } },
);3. OIDC 登录(4 种模式)
// mode 由 SDK 内部按 isMobileUA 推导,业务方不用自己选;按返回值 mode 决定开窗策略
const { url, mode } = await auth.login.startOidc('google', {
completeUrl: 'https://www.tripo3d.ai/login/oidc', // 必填,绝对 URL
returnTo: 'https://studio.tripo3d.ai/dashboard',
});
if (mode === 'popup') {
window.open(url, '_blank', 'popup,width=500,height=600');
} else {
window.location.assign(url);
}不论哪种模式,OIDC 三方回调后落到 OidcCompletePage,SDK 自动写 cookie 信号;主窗的 onLogin(handler) 收到信号触发业务侧 refresh token / 关 dialog。
4. 全局监听登录信号
const unsubscribe = auth.signal.onLogin(async ({ signal, context }) => {
// SDK 在派发前已 await token.refresh() 兜底;业务侧 token.get() 直接拿得到
await fetchCurrentUser();
if (context.isRedirect) {
// 业务页冷启路径(独立页 / OIDC redirect 跳回来的)
}
});onLogin 通过 cookie polling + 启动时 cookie 捕获双通道:
- 同标签登录立即触发
- 跨标签共享同一 cookie 待办,由其中一个实例认领;不保证每个 tab 都触发
- 跨子域(如独立登录页 → 业务子域)通过同 site cookie 触发
- 业务页冷启时构造期
readSignalRecord()捕获未派发信号;有效窗口内每个晚注册的onLoginhandler 都通过 microtask 收到一次定向补偿(context.isRedirect=true)
若需要所有已打开 tab 立即刷新用户态,请在收到 onLogin 后由宿主通过 BroadcastChannel 或 storage 事件二次广播;否则其他 tab 会在后续 session/token 请求中自然感知。
5. Token 自定义来源(业务自家网关)
const auth = new TripoAuth({
basePath,
// 默认走 ory toSession;业务有自家 BFF 网关时覆盖
fetchToken: async (fetcher) => {
const res = await fetcher(`${apiUrl}/whoami?tokenizeAs=default_jwt`, { method: 'GET' });
return res.ok ? (await res.json()).tokenized : undefined;
},
});