refactor(auth): switch to class-based social login and v4 route handler
Migrate from function-based social login helpers (`facebookLogin`, `googleLogin`) to class-based services (`new FacebookLogin().signIn()`, `new GoogleLogin().signIn()`), updating call sites in `AuthFacebookPanel` and `SplashButton` with try/catch error handling. The NextAuth route handler is also refactored to the v4 pattern, importing pre-built `GET` / `POST` exports from `@/lib/auth/nextauth` instead of constructing the handler inline with `NextAuth(authOptions)`.
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
/**
|
||||
* NextAuth App Router 入口
|
||||
* NextAuth App Router 入口(v4 模式)
|
||||
*
|
||||
* 路径:/api/auth/[...nextauth]
|
||||
* 自动处理 NextAuth 所有内置路由:
|
||||
* v4 用单个 handler 函数,需手动 re-export 为 `GET` / `POST`:
|
||||
* /api/auth/signin/[provider]
|
||||
* /api/auth/callback/[provider]
|
||||
* /api/auth/signout
|
||||
@@ -10,10 +9,6 @@
|
||||
* /api/auth/csrf
|
||||
* /api/auth/providers
|
||||
*/
|
||||
import NextAuth from "next-auth";
|
||||
import { GET, POST } from "@/lib/auth/nextauth";
|
||||
|
||||
import { authOptions } from "@/lib/auth/config";
|
||||
|
||||
const handler = NextAuth(authOptions);
|
||||
|
||||
export { handler as GET, handler as POST };
|
||||
export { GET, POST };
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* 路由信号 cookie 管理端点
|
||||
*
|
||||
* 用途:写/清 `cozsweet_authed` httpOnly cookie,作为 `src/router/proxy.ts`
|
||||
* 的"用户已登录"信号源。
|
||||
*
|
||||
* 与 `next-auth.session-token` 的区别:
|
||||
* - NextAuth cookie 仅社交登录设置,邮箱登录不写入
|
||||
* - 本端点由所有登录出口(社交 / 邮箱)统一调用,proxy 只需读这一个 cookie
|
||||
*
|
||||
* 调用方:
|
||||
* - 社交登录出口(nextauth-helpers.persistBackendSession,**server 端**直接用
|
||||
* `next/headers.cookies()` 写,不走本端点)
|
||||
* - 邮箱登录出口(auth-machine.emailLoginActor / emailRegisterThenLoginActor,
|
||||
* **client 端** fetch POST)
|
||||
* - 登出(nextauth-helpers.logout,client 端 fetch DELETE)
|
||||
*
|
||||
* 安全:cookie 设为 `HttpOnly` + `SameSite=Lax`,prod 额外 `Secure`。
|
||||
*/
|
||||
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
const COOKIE_NAME = "cozsweet_authed";
|
||||
const ONE_WEEK = 60 * 60 * 24 * 7;
|
||||
|
||||
/** 设置 httpOnly 路由信号 cookie(任何登录成功后调用) */
|
||||
export async function POST() {
|
||||
const res = NextResponse.json({ ok: true });
|
||||
res.cookies.set({
|
||||
name: COOKIE_NAME,
|
||||
value: "1",
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
path: "/",
|
||||
maxAge: ONE_WEEK,
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 清除路由信号 cookie(登出时调用) */
|
||||
export async function DELETE() {
|
||||
const res = NextResponse.json({ ok: true });
|
||||
res.cookies.set({
|
||||
name: COOKIE_NAME,
|
||||
value: "",
|
||||
path: "/",
|
||||
maxAge: 0,
|
||||
});
|
||||
return res;
|
||||
}
|
||||
@@ -7,13 +7,13 @@
|
||||
* 视觉规格(与 Dart 对齐):
|
||||
* - Spacer → logo(120h)→ Spacer → Facebook 主按钮 → 链接 → Spacer → 法律
|
||||
* - Facebook 主按钮:46px 白胶囊 + Facebook "f" 图标(#1877f2)+ "Login with Facebook"
|
||||
* - 社交登录走 NextAuth:`facebookLogin()` / `googleLogin()`
|
||||
* - 社交登录走 NextAuth:`new FacebookLogin().signIn()` / `new GoogleLogin().signIn()`
|
||||
* - "Other Sign-in Options" 链接打开底部弹层
|
||||
* - 弹层 Email 按钮 → 触发 `onSwitchToEmail`(父级派发 `AuthPanelModeChanged`)
|
||||
*/
|
||||
import { useState } from "react";
|
||||
|
||||
import { facebookLogin, googleLogin } from "@/lib/auth/nextauth";
|
||||
import { FacebookLogin, GoogleLogin } from "@/lib/auth/nextauth";
|
||||
|
||||
import { AuthErrorMessage } from "./auth-error-message";
|
||||
import { AuthLegalText } from "./auth-legal-text";
|
||||
@@ -32,17 +32,25 @@ export function AuthFacebookPanel({ onSwitchToEmail }: AuthFacebookPanelProps) {
|
||||
const handleFacebook = async () => {
|
||||
setBusy("facebook");
|
||||
setError(null);
|
||||
const r = await facebookLogin();
|
||||
setBusy(null);
|
||||
if (!r.success) setError(r.error?.message ?? "Facebook login failed");
|
||||
try {
|
||||
await new FacebookLogin().signIn();
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "Facebook login failed");
|
||||
} finally {
|
||||
setBusy(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoogle = async () => {
|
||||
setBusy("google");
|
||||
setError(null);
|
||||
const r = await googleLogin();
|
||||
setBusy(null);
|
||||
if (!r.success) setError(r.error?.message ?? "Google login failed");
|
||||
try {
|
||||
await new GoogleLogin().signIn();
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "Google login failed");
|
||||
} finally {
|
||||
setBusy(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* - 消费 `useChatDispatch` / `useUserDispatch`(登录成功后初始化)
|
||||
* - 自管路由跳转(已登录 / 登录成功 → /chat)
|
||||
* - Skip 用 `<Link>` 保留 SPA 体验
|
||||
* - 社交登录按钮直接调 `nextauth.facebookLogin()`(NextAuth 流程)
|
||||
* - 社交登录按钮走 `new FacebookLogin().signIn()`(NextAuth 流程)
|
||||
*/
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
@@ -20,7 +20,7 @@ import { useEffect, useRef } from "react";
|
||||
import { useAuthState } from "@/stores/auth/auth-context";
|
||||
import { useChatDispatch } from "@/stores/chat/chat-context";
|
||||
import { useUserDispatch } from "@/stores/user/user-context";
|
||||
import { facebookLogin } from "@/lib/auth/nextauth";
|
||||
import { FacebookLogin } from "@/lib/auth/nextauth";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
import { LoadingIndicator } from "@/app/_components/core/loading-indicator";
|
||||
import styles from "./splash-button.module.css";
|
||||
@@ -57,9 +57,10 @@ export function SplashButton({ skipHref = ROUTES.chat }: SplashButtonProps) {
|
||||
}, [state.isSuccess, chatDispatch, userDispatch, router]);
|
||||
|
||||
const handleFacebookLogin = async () => {
|
||||
const r = await facebookLogin();
|
||||
if (!r.success) {
|
||||
console.error("[splash-button] Facebook login failed", r.error);
|
||||
try {
|
||||
await new FacebookLogin().signIn();
|
||||
} catch (e) {
|
||||
console.error("[splash-button] Facebook login failed", e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user