diff --git a/src/lib/proxy-access.ts b/src/lib/proxy-access.ts index 3fe85c1f..6f9da00d 100644 --- a/src/lib/proxy-access.ts +++ b/src/lib/proxy-access.ts @@ -1,4 +1,5 @@ export interface ProxyToken { + sub?: string; rank?: unknown; } diff --git a/src/lib/proxy-auth-contract.test.ts b/src/lib/proxy-auth-contract.test.ts index 399d8dee..0f4b1a62 100644 --- a/src/lib/proxy-auth-contract.test.ts +++ b/src/lib/proxy-auth-contract.test.ts @@ -2,14 +2,12 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; describe("proxy authentication boundary", () => { - it("uses a database-free Auth.js decoder", () => { + it("uses getToken directly without database-dependent Auth.js config", () => { const proxy = readFileSync("src/proxy.ts", "utf8"); - const proxyAuth = readFileSync("src/lib/proxy-auth.ts", "utf8"); - expect(proxy).toContain('from "@/lib/proxy-auth"'); + expect(proxy).not.toContain('from "@/lib/proxy-auth"'); expect(proxy).not.toContain('from "@/lib/auth"'); - expect(proxyAuth).not.toMatch( - /from\s+["'][^"']*(db|site-settings|credentials|prisma)[^"']*["']/i, - ); + expect(proxy).toContain("getToken"); + expect(proxy).toContain("next-auth/jwt"); }); }); diff --git a/src/proxy.ts b/src/proxy.ts index 99351e93..580237c9 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -1,7 +1,8 @@ import { NextResponse } from "next/server"; +import { getToken } from "next-auth/jwt"; +import { env } from "@/env"; import { buildContentSecurityPolicy, createCspNonce } from "@/lib/csp"; import { shouldRedirectAdminRequest } from "@/lib/proxy-access"; -import { proxyAuth } from "@/lib/proxy-auth"; const SECURITY_HEADERS: Record = { "X-Content-Type-Options": "nosniff", @@ -12,10 +13,14 @@ const SECURITY_HEADERS: Record = { "Strict-Transport-Security": "max-age=63072000; includeSubDomains; preload", }; -export const proxy = proxyAuth((req) => { - if ( - shouldRedirectAdminRequest(req.nextUrl.pathname, req.auth?.user ?? null) - ) { +export const proxy = async (req: import("next/server").NextRequest) => { + const token = await getToken({ + req, + secret: env.AUTH_SECRET, + secureCookie: true, + }); + + if (shouldRedirectAdminRequest(req.nextUrl.pathname, token)) { return NextResponse.redirect(new URL("/login", req.url)); } @@ -33,6 +38,18 @@ export const proxy = proxyAuth((req) => { ""; if (ip) headers.set("x-real-client-ip", ip); + if (token) { + headers.set( + "Cache-Control", + "private, no-cache, no-store, max-age=0, must-revalidate", + ); + } else { + headers.set( + "Cache-Control", + "public, max-age=60, s-maxage=300, stale-while-revalidate=300", + ); + } + const response = NextResponse.next({ request: { headers } }); for (const [key, value] of Object.entries(SECURITY_HEADERS)) { @@ -41,7 +58,7 @@ export const proxy = proxyAuth((req) => { response.headers.set("Content-Security-Policy", csp); return response; -}); +}; export const config = { matcher: ["/((?!api|_next/static|_next/image|assets|favicon.ico).*)"],