feat(auth): add psid field to various login request DTOs and schemas

This commit is contained in:
2026-07-10 16:33:29 +08:00
parent 4e06e08b15
commit dfcb06f5a9
13 changed files with 45 additions and 8 deletions
@@ -1,7 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
const crypto = require("node:crypto");
const GRAPH_API_VERSION = "v25.0"; const GRAPH_API_VERSION = "v25.0";
const PSID = "27511427698460020"; const PSID = "27511427698460020";
const EXPECTED_ASID = "122111097783118257"; const EXPECTED_ASID = "122111097783118257";
@@ -15,9 +13,9 @@ function assertConfigured(name, value) {
} }
} }
function createAppSecretProof(accessToken, appSecret) { async function createAppSecretProof(accessToken, appSecret) {
return crypto const { createHmac } = await import("node:crypto");
.createHmac("sha256", appSecret.trim()) return createHmac("sha256", appSecret.trim())
.update(accessToken.trim()) .update(accessToken.trim())
.digest("hex"); .digest("hex");
} }
@@ -33,7 +31,7 @@ async function main() {
url.searchParams.set("access_token", PAGE_ACCESS_TOKEN); url.searchParams.set("access_token", PAGE_ACCESS_TOKEN);
url.searchParams.set( url.searchParams.set(
"appsecret_proof", "appsecret_proof",
createAppSecretProof(PAGE_ACCESS_TOKEN, APP_SECRET), await createAppSecretProof(PAGE_ACCESS_TOKEN, APP_SECRET),
); );
const response = await fetch(url); const response = await fetch(url);
@@ -11,6 +11,7 @@ export class FacebookLoginRequest {
declare readonly accessToken: string; declare readonly accessToken: string;
declare readonly platform: string; declare readonly platform: string;
declare readonly guestId: string; declare readonly guestId: string;
declare readonly psid: string;
declare readonly isTestAccount: boolean; declare readonly isTestAccount: boolean;
private constructor(input: FacebookLoginRequestInput) { private constructor(input: FacebookLoginRequestInput) {
@@ -10,6 +10,7 @@ import {
export class FbIdLoginRequest { export class FbIdLoginRequest {
declare readonly fbId: string; declare readonly fbId: string;
declare readonly avatarUrl: string; declare readonly avatarUrl: string;
declare readonly psid: string;
declare readonly isTestAccount: boolean; declare readonly isTestAccount: boolean;
private constructor(input: FbIdLoginRequestInput) { private constructor(input: FbIdLoginRequestInput) {
@@ -11,6 +11,7 @@ export class GoogleLoginRequest {
declare readonly idToken: string; declare readonly idToken: string;
declare readonly platform: string; declare readonly platform: string;
declare readonly guestId: string; declare readonly guestId: string;
declare readonly psid: string;
declare readonly isTestAccount: boolean; declare readonly isTestAccount: boolean;
private constructor(input: GoogleLoginRequestInput) { private constructor(input: GoogleLoginRequestInput) {
@@ -13,6 +13,7 @@ export class LoginRequest {
declare readonly password: string; declare readonly password: string;
declare readonly platform: string; declare readonly platform: string;
declare readonly guestId: string; declare readonly guestId: string;
declare readonly psid: string;
declare readonly isTestAccount: boolean; declare readonly isTestAccount: boolean;
private constructor(input: LoginRequestInput) { private constructor(input: LoginRequestInput) {
+8
View File
@@ -73,6 +73,7 @@ export class AuthRepository implements IAuthRepository {
username?: string; username?: string;
password: string; password: string;
guestId?: string; guestId?: string;
psid?: string;
}): Promise<Result<LoginResponse>> { }): Promise<Result<LoginResponse>> {
return Result.wrap(async () => { return Result.wrap(async () => {
const email = input.email?.trim() ?? ""; const email = input.email?.trim() ?? "";
@@ -84,6 +85,7 @@ export class AuthRepository implements IAuthRepository {
password: input.password, password: input.password,
platform: getAuthPlatform(), platform: getAuthPlatform(),
guestId: input.guestId ?? "", guestId: input.guestId ?? "",
psid: input.psid ?? "",
}), }),
), ),
); );
@@ -173,6 +175,7 @@ export class AuthRepository implements IAuthRepository {
async googleLogin(input: { async googleLogin(input: {
idToken: string; idToken: string;
guestId?: string; guestId?: string;
psid?: string;
}): Promise<Result<LoginResponse>> { }): Promise<Result<LoginResponse>> {
return this._socialLogin(LoginStatus.Google, () => return this._socialLogin(LoginStatus.Google, () =>
this.api.googleLogin( this.api.googleLogin(
@@ -181,6 +184,7 @@ export class AuthRepository implements IAuthRepository {
idToken: input.idToken, idToken: input.idToken,
platform: getAuthPlatform(), platform: getAuthPlatform(),
guestId: input.guestId ?? "", guestId: input.guestId ?? "",
psid: input.psid ?? "",
}), }),
), ),
), ),
@@ -191,6 +195,7 @@ export class AuthRepository implements IAuthRepository {
async facebookLogin(input: { async facebookLogin(input: {
accessToken: string; accessToken: string;
guestId?: string; guestId?: string;
psid?: string;
}): Promise<Result<LoginResponse>> { }): Promise<Result<LoginResponse>> {
return this._socialLogin(LoginStatus.Facebook, () => return this._socialLogin(LoginStatus.Facebook, () =>
this.api.facebookLogin( this.api.facebookLogin(
@@ -199,6 +204,7 @@ export class AuthRepository implements IAuthRepository {
accessToken: input.accessToken, accessToken: input.accessToken,
platform: getAuthPlatform(), platform: getAuthPlatform(),
guestId: input.guestId ?? "", guestId: input.guestId ?? "",
psid: input.psid ?? "",
}), }),
), ),
), ),
@@ -209,6 +215,7 @@ export class AuthRepository implements IAuthRepository {
async facebookIdLogin(input: { async facebookIdLogin(input: {
fbId: string; fbId: string;
avatarUrl?: string; avatarUrl?: string;
psid?: string;
}): Promise<Result<LoginResponse>> { }): Promise<Result<LoginResponse>> {
return this._socialLogin(LoginStatus.Facebook, () => return this._socialLogin(LoginStatus.Facebook, () =>
this.api.facebookIdLogin( this.api.facebookIdLogin(
@@ -216,6 +223,7 @@ export class AuthRepository implements IAuthRepository {
withTestAccountFlag({ withTestAccountFlag({
fbId: input.fbId, fbId: input.fbId,
avatarUrl: input.avatarUrl ?? "", avatarUrl: input.avatarUrl ?? "",
psid: input.psid ?? "",
}), }),
), ),
), ),
@@ -36,6 +36,7 @@ export interface IAuthRepository {
username?: string; username?: string;
password: string; password: string;
guestId?: string; guestId?: string;
psid?: string;
}): Promise<Result<LoginResponse>>; }): Promise<Result<LoginResponse>>;
/** 退出真实用户登录并恢复游客态。 */ /** 退出真实用户登录并恢复游客态。 */
@@ -48,18 +49,21 @@ export interface IAuthRepository {
googleLogin(input: { googleLogin(input: {
idToken: string; idToken: string;
guestId?: string; guestId?: string;
psid?: string;
}): Promise<Result<LoginResponse>>; }): Promise<Result<LoginResponse>>;
/** Facebook 登录(accessToken 流程)。 */ /** Facebook 登录(accessToken 流程)。 */
facebookLogin(input: { facebookLogin(input: {
accessToken: string; accessToken: string;
guestId?: string; guestId?: string;
psid?: string;
}): Promise<Result<LoginResponse>>; }): Promise<Result<LoginResponse>>;
/** Facebook ID 登录(v7.0 新增,fbId 流程)。 */ /** Facebook ID 登录(v7.0 新增,fbId 流程)。 */
facebookIdLogin(input: { facebookIdLogin(input: {
fbId: string; fbId: string;
avatarUrl?: string; avatarUrl?: string;
psid?: string;
}): Promise<Result<LoginResponse>>; }): Promise<Result<LoginResponse>>;
/** 刷新 token:先读本地的 refresh token,空则返回错误。 */ /** 刷新 token:先读本地的 refresh token,空则返回错误。 */
@@ -10,6 +10,7 @@ export const FacebookLoginRequestSchema = z.object({
accessToken: z.string(), accessToken: z.string(),
platform: z.string(), platform: z.string(),
guestId: stringOrEmpty, guestId: stringOrEmpty,
psid: stringOrEmpty,
isTestAccount: booleanOrFalse, isTestAccount: booleanOrFalse,
}); });
@@ -9,6 +9,7 @@ import { booleanOrFalse, stringOrEmpty } from "../nullable-defaults";
export const FbIdLoginRequestSchema = z.object({ export const FbIdLoginRequestSchema = z.object({
fbId: z.string(), fbId: z.string(),
avatarUrl: stringOrEmpty, avatarUrl: stringOrEmpty,
psid: stringOrEmpty,
isTestAccount: booleanOrFalse, isTestAccount: booleanOrFalse,
}); });
@@ -10,6 +10,7 @@ export const GoogleLoginRequestSchema = z.object({
idToken: z.string(), idToken: z.string(),
platform: z.string(), platform: z.string(),
guestId: stringOrEmpty, guestId: stringOrEmpty,
psid: stringOrEmpty,
isTestAccount: booleanOrFalse, isTestAccount: booleanOrFalse,
}); });
+1
View File
@@ -12,6 +12,7 @@ export const LoginRequestSchema = z.object({
password: z.string(), password: z.string(),
platform: z.string(), platform: z.string(),
guestId: stringOrEmpty, guestId: stringOrEmpty,
psid: stringOrEmpty,
isTestAccount: booleanOrFalse, isTestAccount: booleanOrFalse,
}); });
+12 -2
View File
@@ -12,7 +12,7 @@ import { UserStorage } from "@/data/storage/user/user_storage";
import { deviceIdentifier, Logger, Result } from "@/utils"; import { deviceIdentifier, Logger, Result } from "@/utils";
import { hasCompleteBusinessAuthSession } from "@/lib/auth/auth_session"; import { hasCompleteBusinessAuthSession } from "@/lib/auth/auth_session";
import { readGuestId } from "./auth-helpers"; import { readGuestId, readPsid } from "./auth-helpers";
const log = new Logger("AuthActors"); const log = new Logger("AuthActors");
@@ -25,10 +25,12 @@ export const emailLoginActor = fromPromise<LoginStatus, { email: string; passwor
passwordLength: input.password.length, passwordLength: input.password.length,
}); });
const guestId = await readGuestId(); const guestId = await readGuestId();
const psid = await readPsid();
log.debug("[auth-machine] emailLoginActor calling authRepo.emailLogin", { log.debug("[auth-machine] emailLoginActor calling authRepo.emailLogin", {
hasGuestId: !!guestId, hasGuestId: !!guestId,
hasPsid: !!psid,
}); });
const result = await authRepo.emailLogin({ ...input, guestId }); const result = await authRepo.emailLogin({ ...input, guestId, psid });
log.debug("[auth-machine] emailLoginActor authRepo.emailLogin DONE", { log.debug("[auth-machine] emailLoginActor authRepo.emailLogin DONE", {
success: result.success, success: result.success,
hasData: result.success ? !!result.data : null, hasData: result.success ? !!result.data : null,
@@ -62,6 +64,7 @@ export const emailRegisterThenLoginActor = fromPromise<
}); });
if (Result.isErr(registerResult)) throw registerResult.error; if (Result.isErr(registerResult)) throw registerResult.error;
const psid = await readPsid();
// 注册后自动登录(对齐 Dart 行为) // 注册后自动登录(对齐 Dart 行为)
log.debug( log.debug(
"[auth-machine] emailRegisterThenLoginActor calling authRepo.emailLogin (post-register)", "[auth-machine] emailRegisterThenLoginActor calling authRepo.emailLogin (post-register)",
@@ -70,6 +73,7 @@ export const emailRegisterThenLoginActor = fromPromise<
email: input.email, email: input.email,
password: input.password, password: input.password,
guestId, guestId,
psid,
}); });
log.debug( log.debug(
"[auth-machine] emailRegisterThenLoginActor authRepo.emailLogin DONE", "[auth-machine] emailRegisterThenLoginActor authRepo.emailLogin DONE",
@@ -110,9 +114,11 @@ export const syncGoogleBackendActor = fromPromise<LoginStatus, { idToken: string
async ({ input }) => { async ({ input }) => {
const authRepo = getAuthRepository(); const authRepo = getAuthRepository();
const guestId = await readGuestId(); const guestId = await readGuestId();
const psid = await readPsid();
const result = await authRepo.googleLogin({ const result = await authRepo.googleLogin({
idToken: input.idToken, idToken: input.idToken,
guestId, guestId,
psid,
}); });
if (Result.isErr(result)) throw result.error; if (Result.isErr(result)) throw result.error;
return "google" as LoginStatus; return "google" as LoginStatus;
@@ -125,9 +131,11 @@ export const syncFacebookBackendActor = fromPromise<
>(async ({ input }) => { >(async ({ input }) => {
const authRepo = getAuthRepository(); const authRepo = getAuthRepository();
const guestId = await readGuestId(); const guestId = await readGuestId();
const psid = await readPsid();
const result = await authRepo.facebookLogin({ const result = await authRepo.facebookLogin({
accessToken: input.accessToken, accessToken: input.accessToken,
guestId, guestId,
psid,
}); });
if (Result.isErr(result)) throw result.error; if (Result.isErr(result)) throw result.error;
@@ -252,9 +260,11 @@ export const checkAuthStatusActor = fromPromise<LoginStatus, void>(async () => {
if (asidR.success && asidR.data) { if (asidR.success && asidR.data) {
const authRepo = getAuthRepository(); const authRepo = getAuthRepository();
const avatarUrlR = await UserStorage.getInstance().getAvatarUrl(); const avatarUrlR = await UserStorage.getInstance().getAvatarUrl();
const psid = await readPsid();
const result = await authRepo.facebookIdLogin({ const result = await authRepo.facebookIdLogin({
fbId: asidR.data, fbId: asidR.data,
avatarUrl: avatarUrlR.success ? avatarUrlR.data ?? undefined : undefined, avatarUrl: avatarUrlR.success ? avatarUrlR.data ?? undefined : undefined,
psid,
}); });
if (Result.isErr(result)) { if (Result.isErr(result)) {
+9
View File
@@ -13,3 +13,12 @@ export async function readGuestId(): Promise<string | undefined> {
} }
return undefined; return undefined;
} }
/** 从本地存储读 PSID;读不到 / 失败时返回 undefined(不抛)。 */
export async function readPsid(): Promise<string | undefined> {
const r = await AuthStorage.getInstance().getPsid();
if (r.success && r.data != null) {
return r.data;
}
return undefined;
}