refactor(auth): migrate social login to next-auth

This commit is contained in:
2026-06-10 10:26:45 +08:00
parent 109a3e3855
commit a4b902893e
34 changed files with 572 additions and 1421 deletions
@@ -1,242 +0,0 @@
"use client";
/**
* 第三方登录 Bridge 组件。
*
* 原始 Dart: lib/utils/google_auth_initializer.dart + lib/utils/facebook_auth_initializer.dart
*
* 设计:
* - 不渲染任何 UI`children` 透传)。
* - 通过 `@react-oauth/google` 的 `GoogleOAuthProvider` 加载的 GIS 脚本,
* 调用 `window.google.accounts.id.initialize` + `prompt` 实现 Google 登录弹窗;
* callback 收到 id_token 后解析 JWT payload 拿到 email/displayName/photoUrl。
* - 通过 `@greatsumini/react-facebook-login` 的 `FacebookLoginClient.init` +
* `FacebookLoginClient.login` 实现 Facebook 登录弹窗。
* - 监听 `auth_event_bridge` 派发的 `cozsweet:auth:*:start` 事件,触发对应
* SDK 流程;拿到凭证后回调 `platform.resolveGoogle()` / `platform.resolveFacebook()`。
* - 单 popup 语义:v1 一次只追踪一个待处理 Google / Facebook 请求,多个并发
* 调用会被覆盖。后续如需并发支持,需把 ref 改为 `Map<requestId, resolver>`。
*
* 部署:本步骤**不**挂载到 `src/app/layout.tsx`。下一轮 UI 接入时按如下方式挂载:
*
* import { GoogleOAuthProvider } from "@react-oauth/google";
* import { AuthPlatformBridge } from "@/data/services/auth";
*
* <GoogleOAuthProvider clientId={process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!}>
* <AuthPlatformBridge platform={getAuthPlatform({...})}>
* {children}
* </AuthPlatformBridge>
* </GoogleOAuthProvider>
*/
import { useEffect, useRef } from "react";
import { useGoogleOAuth } from "@react-oauth/google";
import { FacebookLoginClient, LoginStatus } from "@greatsumini/react-facebook-login";
import { Result, toError, type Result as ResultT } from "@/utils/result";
import type {
IAuthPlatform,
IAuthPlatformOptions,
GoogleCredential,
FacebookLoginResult,
} from "./auth_platform";
import { AUTH_EVENTS, onAuthEvent } from "./auth_event_bridge";
/**
* Bridge 要求 platform 是带 `opts` + `resolveGoogle` + `resolveFacebook` 的
* WebAuthPlatform。结构化类型便于 mock / 子类扩展。
*/
export interface AuthPlatformBridgeProps {
platform: IAuthPlatform & {
readonly opts: IAuthPlatformOptions;
resolveGoogle(
requestId: string,
result: ResultT<GoogleCredential>,
): void;
resolveFacebook(
requestId: string,
result: ResultT<FacebookLoginResult>,
): void;
};
children?: React.ReactNode;
}
export function AuthPlatformBridge({
platform,
children,
}: AuthPlatformBridgeProps) {
const platformRef = useRef(platform);
// 在 effect 中更新 refreact-hooks/refs 规则禁止 render 期更新 ref.current)。
// 这样后续 effect / callback 闭包总能拿到最新的 platform 引用。
useEffect(() => {
platformRef.current = platform;
});
// ======== Google GIS ========
const { scriptLoadedSuccessfully } = useGoogleOAuth();
const pendingGoogleRef = useRef<{ requestId: string } | null>(null);
// 初始化 GIS callback(一次性;GIS 全局只允许一个 id initialize 回调)
useEffect(() => {
if (!scriptLoadedSuccessfully) return;
if (typeof window === "undefined") return;
const gis = window.google?.accounts?.id;
if (!gis) return;
gis.initialize({
client_id: platformRef.current.opts.googleClientId,
callback: (response) => {
const pending = pendingGoogleRef.current;
pendingGoogleRef.current = null;
if (!pending) return;
if (response.credential) {
const decoded = decodeJwtPayload(response.credential);
platformRef.current.resolveGoogle(
pending.requestId,
Result.ok<GoogleCredential>({
idToken: response.credential,
email: decoded?.email ?? null,
displayName: decoded?.name ?? null,
photoUrl: decoded?.picture ?? null,
}),
);
// 全局成功回调
platformRef.current.opts.onGoogleSignInSuccess?.(
response.credential,
decoded?.email ?? null,
);
} else {
platformRef.current.resolveGoogle(
pending.requestId,
Result.err<GoogleCredential>(
new Error("Google sign-in failed: no credential"),
),
);
}
},
});
}, [scriptLoadedSuccessfully]);
// 监听 googleStart 事件 → 触发 prompt
useEffect(() => {
return onAuthEvent(AUTH_EVENTS.googleStart, (detail) => {
pendingGoogleRef.current = { requestId: detail.requestId };
const gis =
typeof window !== "undefined" ? window.google?.accounts?.id : undefined;
if (gis) {
gis.prompt();
} else {
// GIS 未加载完成:立即 reject
pendingGoogleRef.current = null;
platformRef.current.resolveGoogle(
detail.requestId,
Result.err<GoogleCredential>(
new Error("Google Identity Services script not loaded"),
),
);
}
});
}, []);
// ======== Facebook SDK ========
const pendingFacebookRef = useRef<{ requestId: string } | null>(null);
// 初始化 Facebook SDK(一次性)
useEffect(() => {
if (typeof window === "undefined") return;
try {
// 版本联合类型截止 v21.0,但底层 FB JS SDK 支持 v25.0;用类型断言绕过。
FacebookLoginClient.init({
appId: platformRef.current.opts.facebookAppId,
version: (platformRef.current.opts.facebookApiVersion ??
"v25.0") as "v21.0",
cookie: true,
xfbml: true,
});
} catch (e) {
console.warn("[auth] FacebookLoginClient.init failed", e);
}
}, []);
// 监听 facebookStart 事件 → 触发 login
useEffect(() => {
return onAuthEvent(AUTH_EVENTS.facebookStart, (detail) => {
pendingFacebookRef.current = { requestId: detail.requestId };
try {
FacebookLoginClient.login(
(response) => {
const pending = pendingFacebookRef.current;
pendingFacebookRef.current = null;
if (!pending) return;
if (
response.status === LoginStatus.Connected &&
response.authResponse
) {
const expiresIn = Number(response.authResponse.expiresIn);
const expiresAt = Number.isFinite(expiresIn)
? Date.now() + expiresIn * 1000
: null;
platformRef.current.resolveFacebook(
pending.requestId,
Result.ok<FacebookLoginResult>({
status: "success",
accessToken: response.authResponse.accessToken,
userId: response.authResponse.userID,
expiresAt,
}),
);
} else {
// NotAuthorized / Unknown / loginCancelled / facebookNotLoaded
platformRef.current.resolveFacebook(
pending.requestId,
Result.ok<FacebookLoginResult>({
status: "cancelled",
accessToken: "",
userId: "",
expiresAt: null,
}),
);
}
},
{ scope: "public_profile,email" },
);
} catch (e) {
const pending = pendingFacebookRef.current;
pendingFacebookRef.current = null;
if (pending) {
platformRef.current.resolveFacebook(
pending.requestId,
Result.err<FacebookLoginResult>(toError(e)),
);
}
}
});
}, []);
return <>{children}</>;
}
// ======== Helpers ========
/**
* 解码 JWT payload 段。不做签名校验——签名由后端校验。
* 返回 null 表示解码失败。
*/
function decodeJwtPayload(
idToken: string,
): { email?: string; name?: string; picture?: string } | null {
try {
const parts = idToken.split(".");
if (parts.length !== 3) return null;
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
const padded = payload + "=".repeat((4 - (payload.length % 4)) % 4);
const decoded = atob(padded);
return JSON.parse(decoded) as {
email?: string;
name?: string;
picture?: string;
};
} catch {
return null;
}
}
@@ -1,47 +0,0 @@
"use client";
/**
* 跨 plain-TS / React 边界的 DOM 事件通道。
*
* 用途:
* - `WebAuthPlatform`plain TS class,挂在 `getAuthPlatform()` 单例上)dispatch
* `cozsweet:auth:*:start` 事件,并等待 `AuthPlatformBridge`"use client" 组件)
* 调回 `platform.resolveGoogle()` / `platform.resolveFacebook()` 完成 Promise。
* - 这样既满足"接口保持 plain TS、可以被非 React 上下文消费"的需求,又让
* `@react-oauth/google` 的 `GoogleOAuthProvider`(脚本加载)和
* `window.google.accounts.id.*` GIS API 都在 React 侧执行。
*
* 本文件不依赖 React,可以被 `WebAuthPlatform`(无 React 引用)安全 import。
*/
export const AUTH_EVENTS = {
googleStart: "cozsweet:auth:google:start",
facebookStart: "cozsweet:auth:facebook:start",
} as const;
export type AuthEventName = (typeof AUTH_EVENTS)[keyof typeof AUTH_EVENTS];
/** Payload attached to *:start events. */
export interface AuthStartDetail {
readonly requestId: string;
}
export function dispatchAuthEvent(
name: AuthEventName,
detail: AuthStartDetail,
): void {
if (typeof window === "undefined") return;
window.dispatchEvent(new CustomEvent<AuthStartDetail>(name, { detail }));
}
export function onAuthEvent(
name: AuthEventName,
handler: (detail: AuthStartDetail) => void,
): () => void {
if (typeof window === "undefined") return () => {};
const listener = (e: Event) => {
const detail = (e as CustomEvent<AuthStartDetail>).detail;
handler(detail);
};
window.addEventListener(name, listener);
return () => window.removeEventListener(name, listener);
}
-91
View File
@@ -1,91 +0,0 @@
/**
* 第三方登录平台抽象接口 + 工厂 + 单例
*
* 原始 Dart: lib/data/services/auth/auth_platform.dart
*
* 设计:
* - `IAuthPlatform` 是 plain TypeScript 接口(不依赖 React)。
* - 真机实现 `WebAuthPlatform` 通过 DOM 事件与 `AuthPlatformBridge`"use client"
* 组件)通信,由 Bridge 持有 React SDK 钩子。
* - Mock 实现 `MockAuthPlatform` 跳过 SDK,直接返回固定 token。
* - 工厂根据 `NEXT_PUBLIC_USE_MOCK_AUTH` 环境变量二选一(对齐 Dart `kDebugMode`)。
*
* 调用方契约:
* - 平台**只产 token**,不调 `authRepository` / `authApi`。
* - 拿到 `Result<GoogleCredential>` / `Result<FacebookLoginResult>` 后,
* 由调用方自行 `authRepository.googleLogin({ idToken })` 完成登录。
*/
import { Result, type Result as ResultT } from "@/utils/result";
import type { FacebookUserData } from "@/data/dto/auth/facebook_user_data";
import { WebAuthPlatform } from "./web_auth_platform";
import { MockAuthPlatform } from "./mock_auth_platform";
/** 归一化的 Google 凭证(id_token + 解析自 JWT payload 的展示字段)。 */
export interface GoogleCredential {
readonly idToken: string;
readonly email: string | null;
readonly displayName: string | null;
readonly photoUrl: string | null;
}
/** 归一化的 Facebook 登录结果。 */
export interface FacebookLoginResult {
readonly status: "success" | "cancelled" | "error";
readonly accessToken: string;
readonly userId: string;
/** epoch msnull 表示后端未返回 expiresIn。 */
readonly expiresAt: number | null;
}
export interface IAuthPlatform {
/** Google 登录。Web 平台通过 GIS 弹窗流程。 */
googleSignIn(): Promise<ResultT<GoogleCredential>>;
/** 是否支持 Google "authenticate" 流程。Web 平台恒为 false。 */
supportsGoogleAuthenticate(): boolean;
/** Facebook 登录。 */
facebookSignIn(): Promise<ResultT<FacebookLoginResult>>;
/** 拉取 Facebook 用户数据(id / name / email / pictureUrl)。 */
getFacebookUserData(): Promise<ResultT<FacebookUserData>>;
}
export interface IAuthPlatformOptions {
readonly googleClientId: string;
readonly facebookAppId: string;
/** Facebook Graph API 版本,默认 "v25.0"(对齐 Flutter 端)。 */
readonly facebookApiVersion?: string;
/** 全局 Google 登录成功回调(对齐 Dart `onGoogleSignInSuccess`)。 */
readonly onGoogleSignInSuccess?: (
idToken: string,
email: string | null,
) => void;
}
export class AuthPlatformFactory {
private constructor() {}
/**
* 根据 `NEXT_PUBLIC_USE_MOCK_AUTH` 二选一返回平台实现。
* 对齐 Dart `AuthPlatformFactory.create()` 的 `kDebugMode` 分支。
*/
static create(opts: IAuthPlatformOptions): IAuthPlatform {
if (process.env.NEXT_PUBLIC_USE_MOCK_AUTH === "true") {
return new MockAuthPlatform();
}
return new WebAuthPlatform(opts);
}
}
/** 顶层单例(懒加载)。 */
let _instance: IAuthPlatform | null = null;
export function getAuthPlatform(opts: IAuthPlatformOptions): IAuthPlatform {
if (!_instance) _instance = AuthPlatformFactory.create(opts);
return _instance;
}
/** @internal 测试用:重置单例。 */
export function _resetAuthPlatformForTests(): void {
_instance = null;
}
// re-export 让 barrelsby 抓得到
export { Result };
-50
View File
@@ -1,50 +0,0 @@
/**
* 全局类型声明:Google Identity Services (`gis`) 添加的 `window.google` 全局对象。
*
* 由 `<GoogleOAuthProvider>` / `useGoogleOAuth()` 注入的 GIS 脚本会向 `window`
* 挂载 `google.accounts.id` / `google.accounts.oauth2` 等命名空间。本文件让
* TypeScript 知道这些属性的存在,避免在 Bridge 组件中用 `as unknown as ...`
* 强转。
*
* 仅声明我们用到的子集;未列出的 GIS 属性走 `unknown`。
*/
export {};
declare global {
interface Window {
google?: {
accounts?: {
id?: {
initialize(config: {
client_id: string;
callback: (response: { credential?: string }) => void;
auto_select?: boolean;
cancel_on_tap_outside?: boolean;
itp_support?: boolean;
login_hint?: string;
hosted_domain?: string;
use_fedcm_for_prompt?: boolean;
nonce?: string;
state_cookie_domain?: string;
prompt_parent_id?: string;
}): void;
prompt(): void;
renderButton(
parent: HTMLElement,
options: {
type?: "standard" | "icon";
theme?: "outline" | "filled_blue" | "filled_black";
size?: "large" | "medium" | "small";
text?: "signin_with" | "signup_with" | "continue_with" | "signin";
shape?: "rectangular" | "pill" | "circle" | "square";
logo_alignment?: "left" | "center";
width?: string | number;
},
): void;
cancel(): void;
};
oauth2?: unknown;
};
};
}
}
-12
View File
@@ -1,12 +0,0 @@
/**
* @file Automatically generated by barrelsby.
*/
export * from "./auth_bridge_component";
export * from "./auth_event_bridge";
export * from "./auth_platform";
export * from "./global";
export * from "./initialize_facebook_auth";
export * from "./initialize_google_auth";
export * from "./mock_auth_platform";
export * from "./web_auth_platform";
@@ -1,27 +0,0 @@
"use client";
/**
* Facebook SDK 预热辅助。
*
* 原始 Dart: `lib/utils/facebook_auth_initializer.dart` 的 `initializeFacebookAuth()`
*
* 行为:
* - 对齐 Dart `FacebookAuth.i.webAndDesktopInitialize(appId, cookie: true,
* xfbml: true, version: 'v25.0')` 的参数语义。
* - 真实 SDK 初始化(注入 `<div id="facebook-jssdk">` + 调用 `FB.init`)由
* `@greatsumini/react-facebook-login` 的 `FacebookLoginClient.init()` 完成,
* 见 `auth_bridge_component.tsx`。本函数仅做存在性校验 + console.warn。
*/
export function initializeFacebookAuth(
appId: string,
version: string = "v25.0",
): void {
if (typeof window === "undefined") return;
if (!appId) {
console.warn(
"[auth] initializeFacebookAuth: NEXT_PUBLIC_FACEBOOK_APP_ID is not set",
);
}
// version 占位:实际由 FacebookLoginClient.init({ version }) 消费
void version;
}
@@ -1,23 +0,0 @@
"use client";
/**
* Google OAuth 预热辅助。
*
* 原始 Dart: `lib/utils/google_auth_initializer.dart` 的 `initializeGoogleAuth()`
*
* 行为:
* - `@react-oauth/google` 的 `<GoogleOAuthProvider>` 已经负责加载 GIS 脚本;
* 本函数在 script 加载完成时记录一条调试日志(如果 env 缺失则 warn)。
* - 不发起任何网络请求;副作用仅限 console。
*
* 部署:本文件对应 Dart 端 `initializeGoogleAuth()` 调用位点;
* 真实使用时机由调用方在 `useEffect` 中安排,不需要在模块顶层自动执行。
*/
export function initializeGoogleAuth(clientId: string): void {
if (typeof window === "undefined") return;
if (!clientId) {
console.warn(
"[auth] initializeGoogleAuth: NEXT_PUBLIC_GOOGLE_CLIENT_ID is not set",
);
}
}
@@ -1,62 +0,0 @@
"use client";
/**
* 模拟第三方登录平台。
*
* 原始 Dart: lib/data/services/auth/mock_auth_platform.dart
*
* 在 `NEXT_PUBLIC_USE_MOCK_AUTH=true` 时由 `AuthPlatformFactory` 注入。
* 返回固定的 idToken / accessToken / 用户信息,附 250ms 模拟延迟便于观察 loading
* 状态。
*/
import { Result, type Result as ResultT } from "@/utils/result";
import { FacebookUserData } from "@/data/dto/auth/facebook_user_data";
import type {
IAuthPlatform,
GoogleCredential,
FacebookLoginResult,
} from "./auth_platform";
function sleep(ms: number): Promise<void> {
return new Promise((r) => setTimeout(r, ms));
}
export class MockAuthPlatform implements IAuthPlatform {
constructor(private readonly simulatedDelayMs: number = 250) {}
async googleSignIn(): Promise<ResultT<GoogleCredential>> {
await sleep(this.simulatedDelayMs);
return Result.ok({
idToken: `mock_google_id_token_${Date.now()}`,
email: "mock@example.com",
displayName: "Mock User",
photoUrl: "https://via.placeholder.com/150",
});
}
supportsGoogleAuthenticate(): boolean {
return true;
}
async facebookSignIn(): Promise<ResultT<FacebookLoginResult>> {
await sleep(this.simulatedDelayMs);
return Result.ok({
status: "success",
accessToken: `mock_facebook_token_${Date.now()}`,
userId: "mock_facebook_user_id",
expiresAt: Date.now() + 3600_000,
});
}
async getFacebookUserData(): Promise<ResultT<FacebookUserData>> {
await sleep(this.simulatedDelayMs);
return Result.ok(
FacebookUserData.from({
id: "mock_facebook_id",
name: "Mock User",
email: "mock@example.com",
pictureUrl: "https://via.placeholder.com/150",
}),
);
}
}
-195
View File
@@ -1,195 +0,0 @@
"use client";
/**
* Web 平台真机实现。
*
* 原始 Dart: lib/data/services/auth/auth_platform_web.dart
*
* 类本身是 plain TS,不调用 React 钩子;通过 `auth_event_bridge` 派发
* `cozsweet:auth:*:start` 事件,由 `AuthPlatformBridge` 持有 GIS / FB SDK 钩子
* 并在拿到凭证后回调 `platform.resolveGoogle()` / `platform.resolveFacebook()`。
*
* SSR 安全:
* - 构造函数、supportsGoogleAuthenticate() 在 server 端可用;
* - 业务方法在 SSR 下直接返回 failure(与 Flutter WebAuthPlatform 一致:
* 必须由用户点击触发弹窗,服务器侧无法完成 OAuth)。
*/
import { Result, toError, type Result as ResultT } from "@/utils/result";
import { FacebookUserData } from "@/data/dto/auth/facebook_user_data";
import {
AUTH_EVENTS,
dispatchAuthEvent,
} from "./auth_event_bridge";
import type {
IAuthPlatform,
IAuthPlatformOptions,
GoogleCredential,
FacebookLoginResult,
} from "./auth_platform";
const TIMEOUT_MS = 5 * 60 * 1000;
interface PendingResolver<T> {
resolve: (r: ResultT<T>) => void;
}
export class WebAuthPlatform implements IAuthPlatform {
readonly opts: IAuthPlatformOptions;
private readonly pendingGoogle = new Map<
string,
PendingResolver<GoogleCredential>
>();
private readonly pendingFacebook = new Map<
string,
PendingResolver<FacebookLoginResult>
>();
private nextId = 0;
constructor(opts: IAuthPlatformOptions) {
this.opts = opts;
}
/** 由 AuthPlatformBridge 在拿到 GIS 凭证后回调。 */
resolveGoogle(
requestId: string,
result: ResultT<GoogleCredential>,
): void {
const pending = this.pendingGoogle.get(requestId);
if (pending) {
pending.resolve(result);
this.pendingGoogle.delete(requestId);
}
}
/** 由 AuthPlatformBridge 在拿到 FB login response 后回调。 */
resolveFacebook(
requestId: string,
result: ResultT<FacebookLoginResult>,
): void {
const pending = this.pendingFacebook.get(requestId);
if (pending) {
pending.resolve(result);
this.pendingFacebook.delete(requestId);
}
}
async googleSignIn(): Promise<ResultT<GoogleCredential>> {
if (typeof window === "undefined") {
return Result.err(new Error("googleSignIn requires a browser"));
}
if (!this.opts.googleClientId) {
return Result.err(
new Error("NEXT_PUBLIC_GOOGLE_CLIENT_ID is not set"),
);
}
const requestId = this.allocId();
return new Promise<ResultT<GoogleCredential>>((resolve) => {
this.pendingGoogle.set(requestId, { resolve });
dispatchAuthEvent(AUTH_EVENTS.googleStart, { requestId });
setTimeout(() => {
if (this.pendingGoogle.has(requestId)) {
this.pendingGoogle.delete(requestId);
resolve(Result.err(new Error("Google sign-in timed out")));
}
}, TIMEOUT_MS);
});
}
/**
* Web 平台使用 GIS 弹窗流程,`google.accounts.id.prompt()`,不调用
* "authenticate" API。返回 false 以对齐 Dart `WebAuthPlatform` 行为。
*/
supportsGoogleAuthenticate(): boolean {
return false;
}
async facebookSignIn(): Promise<ResultT<FacebookLoginResult>> {
if (typeof window === "undefined") {
return Result.err(new Error("facebookSignIn requires a browser"));
}
if (!this.opts.facebookAppId) {
return Result.err(
new Error("NEXT_PUBLIC_FACEBOOK_APP_ID is not set"),
);
}
const requestId = this.allocId();
return new Promise<ResultT<FacebookLoginResult>>((resolve) => {
this.pendingFacebook.set(requestId, { resolve });
dispatchAuthEvent(AUTH_EVENTS.facebookStart, { requestId });
setTimeout(() => {
if (this.pendingFacebook.has(requestId)) {
this.pendingFacebook.delete(requestId);
resolve(Result.err(new Error("Facebook sign-in timed out")));
}
}, TIMEOUT_MS);
});
}
/**
* 拉取 Facebook 用户数据。
*
* 与 Dart `WebAuthPlatform.getFacebookUserData()` 对齐:调用 FB JS SDK 的
* `FB.api('/me', { fields })`。前提是 Bridge 已经 `FacebookLoginClient.init`
* 初始化过 SDK`window.FB` 全局存在)。若 SDK 未加载则返回 failure。
*/
async getFacebookUserData(): Promise<ResultT<FacebookUserData>> {
if (typeof window === "undefined") {
return Result.err(
new Error("getFacebookUserData requires a browser"),
);
}
const FB = (window as unknown as { FB?: unknown }).FB;
if (!FB) {
return Result.err(
new Error(
"Facebook SDK not loaded; call facebookSignIn() first or mount <AuthPlatformBridge>",
),
);
}
return new Promise<ResultT<FacebookUserData>>((resolve) => {
try {
(window as unknown as {
FB: {
api: (
path: string,
params: Record<string, string>,
cb: (resp: Record<string, unknown>) => void,
) => void;
};
}).FB.api(
"/me",
{ fields: "id,name,email,picture" },
(resp) => {
if (!resp || (resp as { error?: unknown }).error) {
resolve(Result.err(new Error("FB.api /me failed")));
return;
}
const r = resp as {
id?: string;
name?: string;
email?: string;
picture?: { data?: { url?: string } };
};
resolve(
Result.ok(
FacebookUserData.from({
id: r.id ?? null,
name: r.name ?? null,
email: r.email ?? null,
pictureUrl: r.picture?.data?.url ?? null,
}),
),
);
},
);
} catch (e) {
resolve(Result.err(toError(e)));
}
});
}
private allocId(): string {
this.nextId += 1;
return `auth-${Date.now()}-${this.nextId}`;
}
}
+1 -8
View File
@@ -14,14 +14,7 @@ export * from "../../core/net/config/api_config";
export * from "./api/interceptor/auth_refresh_interceptor";
export * from "./api/interceptor/logging_interceptor";
export * from "./api/interceptor/token_interceptor";
export * from "./auth/auth_bridge_component";
export * from "./auth/auth_event_bridge";
export * from "./auth/auth_platform";
export * from "./auth/global";
export * from "./auth/initialize_facebook_auth";
export * from "./auth/initialize_google_auth";
export * from "./auth/mock_auth_platform";
export * from "./auth/web_auth_platform";
// 注:原 ./auth/* 平台适配层已删除(社交登录改由 NextAuth 处理)
export * from "../dto/auth/apple_login_request";
export * from "../dto/auth/facebook_login_request";
export * from "../dto/auth/facebook_user_data";