feat(auth): add facebook identity psid login

This commit is contained in:
2026-07-10 17:07:22 +08:00
parent 2e60bf64e6
commit 53801490e5
28 changed files with 637 additions and 37 deletions
+47 -16
View File
@@ -242,29 +242,60 @@ export const guestLoginActor = fromPromise<LoginStatus, void>(async () => {
// ========== 启动 / 恢复时检查登录态 actor(只查不创) ==========
export const checkAuthStatusActor = fromPromise<LoginStatus, void>(async () => {
// 1. 拿 deviceId(不用于 auto-guest-login,仅供后续登录用)
await deviceIdentifier.getDeviceId();
// 1. 拿启动期可能参与身份同步的本地标识。
const deviceId = await deviceIdentifier.getDeviceId();
const storage = AuthStorage.getInstance();
const asidR = await storage.getAsid();
const psidR = await storage.getPsid();
const asid = asidR.success ? asidR.data : null;
const psid = psidR.success ? psidR.data : null;
const authRepo = getAuthRepository();
// 2. 查业务登录 tokenloginToken 和 refreshToken 必须同时存在。
if (await hasCompleteBusinessAuthSession(storage)) {
const providerR = await storage.getLoginProvider();
if (providerR.success && isBusinessLoginProvider(providerR.data)) {
return providerR.data;
const provider =
providerR.success && isBusinessLoginProvider(providerR.data)
? providerR.data
: ("email" as LoginStatus);
const bindResult = await authRepo.bindFacebookIdentity({
asid: asid ?? undefined,
psid: psid ?? undefined,
});
if (Result.isErr(bindResult)) {
log.warn(
{ err: bindResult.error.message },
"[auth-machine] checkAuthStatusActor: bindFacebookIdentity failed",
);
}
return "email" as LoginStatus;
return provider;
}
// 3. 外部浏览器深链同步:仅 asid 落地时,用它换业务 token
const asidR = await storage.getAsid();
if (asidR.success && asidR.data) {
const authRepo = getAuthRepository();
const avatarUrlR = await UserStorage.getInstance().getAvatarUrl();
const psid = await readPsid();
const result = await authRepo.facebookAsidLogin({
asid: asidR.data,
avatarUrl: avatarUrlR.success ? avatarUrlR.data ?? undefined : undefined,
// 3. 有 PSID 时优先让后端用本地绑定关系直接登录;未匹配则返回游客态
if (psid) {
const psidLoginResult = await authRepo.facebookPsidLogin({
psid,
deviceId,
bindToGuest: true,
});
if (Result.isOk(psidLoginResult)) {
return psidLoginResult.data;
}
log.warn(
{ err: psidLoginResult.error.message },
"[auth-machine] checkAuthStatusActor: facebookPsidLogin failed",
);
}
// 4. 无 PSID 或 PSID 直登失败时,继续用 ASID fallback 换业务 token。
if (asid) {
const avatarUrlR = await UserStorage.getInstance().getAvatarUrl();
const result = await authRepo.facebookAsidLogin({
asid,
avatarUrl: avatarUrlR.success ? avatarUrlR.data ?? undefined : undefined,
psid: psid ?? undefined,
});
if (Result.isErr(result)) {
@@ -278,13 +309,13 @@ export const checkAuthStatusActor = fromPromise<LoginStatus, void>(async () => {
return "facebook" as LoginStatus;
}
// 4. 查 guestToken
// 5. 查 guestToken
const guestTokenR = await storage.getGuestToken();
if (guestTokenR.success && guestTokenR.data) {
return "guest" as LoginStatus;
}
// 5. 都没有 → "notLoggedIn"(保持 splash 页面 + 登录入口)
// 6. 都没有 → "notLoggedIn"(保持 splash 页面 + 登录入口)
return "notLoggedIn" as LoginStatus;
});
+2 -1
View File
@@ -32,7 +32,8 @@ const toAuthErrorMessage = (error: unknown): string =>
//
// 设计:所有登录/同步流跑完都回 `idle`(带正确 `loginStatus`)。
//
// 初始化恢复已有登录态;游客登录必须由用户明确操作触发
// 初始化恢复已有登录态,并按后端 Facebook identity 规则处理绑定 / PSID 直登
// 普通游客登录仍必须由用户明确操作触发;PSID 未匹配时后端可返回游客态。
// ============================================================
export const authMachine = setup({
types: {
+3 -3
View File
@@ -1,11 +1,11 @@
"use client";
/**
* AuthInit 派发器 —— App 启动时读 storage 同步 loginStatus 到状态机
* AuthInit 派发器 —— App 启动时恢复登录态并同步 Facebook identity
*
* 派发时机:仅 mount 时一次(`useEffect` deps = `[dispatch]`,永远不重跑)。
*
* 与 <OAuthSessionSync /> 平级:
* - AuthStatusChecker: 启动时一次性 init"自己启动时同步"
* - AuthStatusChecker: 启动时一次性 init,恢复登录态、绑定 ASID/PSID、尝试 PSID 直登
* - OAuthSessionSync: 持续监听 NextAuth session"别人在写"
*/
import { useEffect } from "react";
@@ -25,4 +25,4 @@ export function AuthStatusChecker() {
// 无 UI
return null;
}
}