展开查看模板代码
// ============================================================
// 推荐模式:每个应用创建自己的 gateway session cookie,
// 不直接依赖共享的 access_token cookie 作为日常登录态。
//
// 原因:access_token cookie 在 .stringzhao.life 域共享,
// 任一子域应用的登录/切换会覆盖其他应用的登录态。
// ============================================================
// --- 1. lib/auth-gateway-session.ts ---
// HMAC-SHA256 签名的 cookie,存储 email + 过期时间
import crypto from "node:crypto";
import { NextResponse } from "next/server";
const COOKIE_NAME = "my_app_gateway_session"; // 每个应用使用不同名称
function secret(): string {
return process.env.AUTH_GATEWAY_SESSION_SECRET || "dev-secret";
}
function sign(payload: string): string {
return crypto.createHmac("sha256", secret())
.update(payload).digest("base64url");
}
export function createSession(email: string, ttl = 43_200): string {
const now = Date.now();
const data = JSON.stringify({
email: email.trim().toLowerCase(),
issuedAt: now,
expiresAt: now + ttl * 1000,
});
const encoded = Buffer.from(data).toString("base64url");
return encoded + "." + sign(encoded);
}
export function verifySession(raw: string) {
const [encoded, sig] = raw.split(".", 2);
if (!encoded || !sig) return null;
const expected = sign(encoded);
try {
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig)))
return null;
} catch { return null; }
const parsed = JSON.parse(
Buffer.from(encoded, "base64url").toString()
);
if (Date.now() > parsed.expiresAt) return null;
return { email: parsed.email };
}
export function applySessionCookie(res: NextResponse, value: string) {
res.cookies.set({
name: COOKIE_NAME, value, path: "/",
httpOnly: true, secure: true, sameSite: "lax",
maxAge: 43_200,
});
}
// --- 2. app/api/auth/session/finalize/route.ts ---
// 回跳后调用此接口:读取共享 access_token → 验签 JWT → 创建本地会话
import { jwtVerify } from "jose";
export async function POST(request: Request) {
// 从 cookie 中读取共享 access_token(一次性使用)
const cookies = request.headers.get("cookie") || "";
const match = cookies.match(/access_token=([^;]+)/);
const accessToken = match ? decodeURIComponent(match[1]) : "";
if (!accessToken) {
return Response.json({ error: "missing_access_token" }, { status: 401 });
}
// JWT 验签
const { payload } = await jwtVerify(accessToken, jwksResolver, {
issuer: process.env.AUTH_ISSUER,
audience: process.env.AUTH_AUDIENCE,
});
// 创建应用自有的 gateway session cookie
const res = NextResponse.json({ ok: true, email: payload.email });
applySessionCookie(res, createSession(payload.email as string));
return res;
}
// --- 3. 中间件或页面:检查 gateway session ---
export async function middleware(request: NextRequest) {
const raw = request.cookies.get(COOKIE_NAME)?.value;
const session = raw ? verifySession(raw) : null;
if (!session) {
return NextResponse.redirect(new URL("/auth/start", request.url));
}
return NextResponse.next();
}