feat(auth): add psid field to various login request DTOs and schemas
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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,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)) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user