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
@@ -0,0 +1,91 @@
import { describe, expect, it } from "vitest";
import {
FacebookIdentityResponse,
FacebookPsidLoginResponse,
LoginRequest,
} from "@/data/dto/auth";
import { UserSchema } from "@/data/schemas/user/user";
describe("facebook identity DTO/schema", () => {
it("parses facebook identity binding responses", () => {
const response = FacebookIdentityResponse.fromJson({
fbAsid: "asid-1",
fbPsid: "psid-1",
facebookBinding: {
conflicts: [],
bound: [{ field: "psid" }],
},
});
expect(response.toJson()).toMatchObject({
fbAsid: "asid-1",
fbPsid: "psid-1",
facebookBinding: {
conflicts: [],
bound: [{ field: "psid" }],
},
});
});
it("parses facebook psid login responses for real users and guests", () => {
expect(
FacebookPsidLoginResponse.fromJson({
token: "login-token",
refreshToken: "refresh-token",
matchedBy: "psid",
fbAsid: "asid-1",
fbPsid: "psid-1",
hasCompleteFacebookIdentity: true,
isGuest: false,
user: { id: "user-1", username: "Elio" },
}).toJson(),
).toMatchObject({
isGuest: false,
hasCompleteFacebookIdentity: true,
user: { id: "user-1" },
});
expect(
FacebookPsidLoginResponse.fromJson({
token: "guest-token",
matchedBy: "guest",
fbPsid: "psid-1",
hasCompleteFacebookIdentity: false,
isGuest: true,
userId: "guest-1",
}).toJson(),
).toMatchObject({
refreshToken: "",
isGuest: true,
userId: "guest-1",
user: null,
});
});
it("keeps psid in request serialization without requiring DTO field declarations", () => {
const request = LoginRequest.from({
email: "user@example.com",
password: "password",
platform: "web",
guestId: "",
psid: "psid-1",
});
expect(request.toJson()).toMatchObject({ psid: "psid-1" });
});
it("accepts facebook identity fields in user schema", () => {
expect(
UserSchema.parse({
id: "user-1",
username: "Elio",
fbAsid: "asid-1",
fbPsid: "psid-1",
}),
).toMatchObject({
fbAsid: "asid-1",
fbPsid: "psid-1",
});
});
});
@@ -0,0 +1,28 @@
/**
* Facebook identity 绑定请求 DTO
*/
import {
FacebookIdentityRequestSchema,
type FacebookIdentityRequestInput,
type FacebookIdentityRequestData,
} from "@/data/schemas/auth/facebook_identity_request";
export class FacebookIdentityRequest {
private constructor(input: FacebookIdentityRequestInput) {
const data = FacebookIdentityRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: FacebookIdentityRequestInput): FacebookIdentityRequest {
return new FacebookIdentityRequest(input);
}
static fromJson(json: unknown): FacebookIdentityRequest {
return FacebookIdentityRequest.from(json as FacebookIdentityRequestInput);
}
toJson(): FacebookIdentityRequestData {
return FacebookIdentityRequestSchema.parse(this);
}
}
@@ -11,7 +11,6 @@ export class FacebookLoginRequest {
declare readonly accessToken: string;
declare readonly platform: string;
declare readonly guestId: string;
declare readonly psid: string;
declare readonly isTestAccount: boolean;
private constructor(input: FacebookLoginRequestInput) {
@@ -0,0 +1,30 @@
/**
* Facebook PSID 登录请求 DTO
*/
import {
FacebookPsidLoginRequestSchema,
type FacebookPsidLoginRequestInput,
type FacebookPsidLoginRequestData,
} from "@/data/schemas/auth/facebook_psid_login_request";
export class FacebookPsidLoginRequest {
private constructor(input: FacebookPsidLoginRequestInput) {
const data = FacebookPsidLoginRequestSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: FacebookPsidLoginRequestInput): FacebookPsidLoginRequest {
return new FacebookPsidLoginRequest(input);
}
static fromJson(json: unknown): FacebookPsidLoginRequest {
return FacebookPsidLoginRequest.from(
json as FacebookPsidLoginRequestInput,
);
}
toJson(): FacebookPsidLoginRequestData {
return FacebookPsidLoginRequestSchema.parse(this);
}
}
@@ -10,7 +10,6 @@ import {
export class FbIdLoginRequest {
declare readonly fbId: string;
declare readonly avatarUrl: string;
declare readonly psid: string;
declare readonly isTestAccount: boolean;
private constructor(input: FbIdLoginRequestInput) {
@@ -11,7 +11,6 @@ export class GoogleLoginRequest {
declare readonly idToken: string;
declare readonly platform: string;
declare readonly guestId: string;
declare readonly psid: string;
declare readonly isTestAccount: boolean;
private constructor(input: GoogleLoginRequestInput) {
+2
View File
@@ -1,4 +1,6 @@
export * from "./facebook_identity_request";
export * from "./facebook_login_request";
export * from "./facebook_psid_login_request";
export * from "./fb_id_login_request";
export * from "./google_login_request";
export * from "./guest_login_request";
@@ -13,7 +13,6 @@ export class LoginRequest {
declare readonly password: string;
declare readonly platform: string;
declare readonly guestId: string;
declare readonly psid: string;
declare readonly isTestAccount: boolean;
private constructor(input: LoginRequestInput) {
@@ -0,0 +1,30 @@
/**
* Facebook identity 绑定响应 DTO
*/
import {
FacebookIdentityResponseSchema,
type FacebookIdentityResponseInput,
type FacebookIdentityResponseData,
} from "@/data/schemas/auth/facebook_identity_response";
export class FacebookIdentityResponse {
private constructor(input: FacebookIdentityResponseInput) {
const data = FacebookIdentityResponseSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(input: FacebookIdentityResponseInput): FacebookIdentityResponse {
return new FacebookIdentityResponse(input);
}
static fromJson(json: unknown): FacebookIdentityResponse {
return FacebookIdentityResponse.from(
json as FacebookIdentityResponseInput,
);
}
toJson(): FacebookIdentityResponseData {
return FacebookIdentityResponseSchema.parse(this);
}
}
@@ -0,0 +1,32 @@
/**
* Facebook PSID 登录响应 DTO
*/
import {
FacebookPsidLoginResponseSchema,
type FacebookPsidLoginResponseInput,
type FacebookPsidLoginResponseData,
} from "@/data/schemas/auth/facebook_psid_login_response";
export class FacebookPsidLoginResponse {
private constructor(input: FacebookPsidLoginResponseInput) {
const data = FacebookPsidLoginResponseSchema.parse(input);
Object.assign(this, data);
Object.freeze(this);
}
static from(
input: FacebookPsidLoginResponseInput,
): FacebookPsidLoginResponse {
return new FacebookPsidLoginResponse(input);
}
static fromJson(json: unknown): FacebookPsidLoginResponse {
return FacebookPsidLoginResponse.from(
json as FacebookPsidLoginResponseInput,
);
}
toJson(): FacebookPsidLoginResponseData {
return FacebookPsidLoginResponseSchema.parse(this);
}
}
+2
View File
@@ -1,3 +1,5 @@
export * from "./facebook_identity_response";
export * from "./facebook_psid_login_response";
export * from "./guest_login_response";
export * from "./login_response";
export * from "./logout_response";