refactor(auth): centralize auth routing via proxy and marker cookie
This commit is contained in:
+28
-19
@@ -1,16 +1,18 @@
|
||||
/**
|
||||
* Next.js 16 Proxy(原 `middleware.ts`)
|
||||
*
|
||||
* 行为:
|
||||
* - 当请求携带 NextAuth `next-auth.session-token` cookie 且命中 `/splash` 或 `/auth`
|
||||
* 时,308 重定向到 `/chat`。
|
||||
* - 其他情况透传(`NextResponse.next()`)。
|
||||
* 行为(鉴权路由统一在此处理,业务层组件不再做 auth-driven 跳转):
|
||||
* - 已登录(`cozsweet_authed` cookie 存在)访问 `/splash` 或 `/auth`
|
||||
* → 308 重定向到 `/chat`
|
||||
* - 未登录(cookie 缺失)访问 `/chat` 或 `/sidebar`
|
||||
* → 308 重定向到 `/splash`(首界面)
|
||||
* - 其他情况透传(`NextResponse.next()`)
|
||||
*
|
||||
* 重要约束:
|
||||
* - **不**对 `/chat` 做反向重定向:游客态是合法用户。
|
||||
* - NextAuth 的 session cookie 由 `src/lib/auth/config.ts` 集中管理;本 proxy 仅做
|
||||
* CDN 友好的"乐观"快路径。
|
||||
* - 真正的鉴权门在 Client 侧 `useAuthGate()`(用 `useSession()`)。
|
||||
* - **不**检查 NextAuth 自带的 `next-auth.session-token`:统一用自定义
|
||||
* `cozsweet_authed` 信号,社交 / 邮箱登录出口都同步设/清这个 cookie。
|
||||
* - 真正的鉴权门在 Client 侧 `useAuthGate()`(用 `useSession()`),用于 UI 状态。
|
||||
* - 路由跳转(**唯一**鉴权跳转点)由本 proxy 集中处理。
|
||||
*
|
||||
* Next 16 重要变更(参见 `node_modules/next/dist/docs/01-app/03-api-reference/03-file-conventions/proxy.md`):
|
||||
* - 文件从 `middleware.ts` 重命名为 `proxy.ts`;`middleware` 仍可用但已 deprecated。
|
||||
@@ -25,36 +27,43 @@ import type { NextRequest } from "next/server";
|
||||
|
||||
import {
|
||||
AUTH_ONLY_ROUTES,
|
||||
PROTECTED_ROUTES,
|
||||
ROUTES,
|
||||
type StaticRoute,
|
||||
} from "@/router/routes";
|
||||
|
||||
const SESSION_COOKIE_NAMES = [
|
||||
"next-auth.session-token",
|
||||
"__Secure-next-auth.session-token",
|
||||
];
|
||||
/** 路由信号 cookie 名(由 `src/app/api/auth/marker/route.ts` 写) */
|
||||
const AUTH_COOKIE = "cozsweet_authed";
|
||||
|
||||
function isAuthOnlyRoute(pathname: string): pathname is StaticRoute {
|
||||
function isAuthOnlyRoute(pathname: string): boolean {
|
||||
return (AUTH_ONLY_ROUTES as readonly string[]).includes(pathname);
|
||||
}
|
||||
|
||||
function isProtectedRoute(pathname: string): boolean {
|
||||
return (PROTECTED_ROUTES as readonly string[]).includes(pathname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy 入口
|
||||
*/
|
||||
export function proxy(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
|
||||
// 检查 NextAuth session cookie 存在性(cookie value 存在即认为已登录)
|
||||
const hasSession = SESSION_COOKIE_NAMES.some(
|
||||
(name) => Boolean(request.cookies.get(name)?.value),
|
||||
);
|
||||
const hasAuth = Boolean(request.cookies.get(AUTH_COOKIE)?.value);
|
||||
|
||||
if (hasSession && isAuthOnlyRoute(pathname)) {
|
||||
// 已登录访问未登录专属页 → /chat
|
||||
if (hasAuth && isAuthOnlyRoute(pathname)) {
|
||||
const url = request.nextUrl.clone();
|
||||
url.pathname = ROUTES.chat;
|
||||
return NextResponse.redirect(url, 308);
|
||||
}
|
||||
|
||||
// 未登录访问登录态专属页 → /splash(首界面)
|
||||
if (!hasAuth && isProtectedRoute(pathname)) {
|
||||
const url = request.nextUrl.clone();
|
||||
url.pathname = ROUTES.splash;
|
||||
return NextResponse.redirect(url, 308);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,12 @@ export const AUTH_ONLY_ROUTES: readonly StaticRoute[] = [
|
||||
ROUTES.auth,
|
||||
] as const;
|
||||
|
||||
/** 仅登录态可见的路由(命中后未登录用户应跳走) */
|
||||
export const PROTECTED_ROUTES: readonly StaticRoute[] = [
|
||||
ROUTES.chat,
|
||||
ROUTES.sidebar,
|
||||
] as const;
|
||||
|
||||
/** 公开路由(游客态也允许访问) */
|
||||
export const PUBLIC_ROUTES: readonly StaticRoute[] = [
|
||||
ROUTES.chat,
|
||||
|
||||
Reference in New Issue
Block a user