Phase 7
This commit is contained in:
189 files changed
+9873
-655
No files matched your search
+47
-1
@@ -18,17 +18,63 @@ const PROTECTED_PREFIXES = ["/staff", "/owner"];
|
||||
// spin up a Postgres connection pool on every request.
|
||||
const { auth } = NextAuth(authConfig);
|
||||
|
||||
/**
|
||||
* Nonce-gated 'strict-dynamic' script-src (Next's own documented pattern)
|
||||
* was tried here first and reverted: verified live against a production
|
||||
* build, it broke every page — Next's own inline RSC-hydration payload
|
||||
* scripts (the `self.__next_f.push(...)` chunks each page ships to
|
||||
* stream in its data) never actually receive the nonce this generates,
|
||||
* so 'strict-dynamic' refuses to run them and the app never hydrates
|
||||
* (confirmed via BAILOUT_TO_CLIENT_SIDE_RENDERING markers with
|
||||
* `"nonce":"$undefined"` in the served HTML). `script-src 'self'
|
||||
* 'unsafe-inline'` is the reliable fallback: Next's own *code* all loads
|
||||
* via external `<script src="/_next/static/...">` tags, which `'self'`
|
||||
* already covers — 'unsafe-inline' is only there for those small inline
|
||||
* data-streaming scripts. `style-src` needs 'unsafe-inline' for the same
|
||||
* reason as ever: this app uses plain React `style={{...}}` props
|
||||
* (progress bars, dynamic widths) throughout, and CSP has no nonce
|
||||
* mechanism for style *attributes* (only for <style> elements).
|
||||
*/
|
||||
function buildCsp(): string {
|
||||
// Fast Refresh/React DevTools' dev-mode debugging needs eval() — React
|
||||
// itself confirms it "will never use eval() in production mode", so
|
||||
// this only loosens the policy for local development, never a real
|
||||
// deployment.
|
||||
const scriptSrc = process.env.NODE_ENV === "production" ? "'self' 'unsafe-inline'" : "'self' 'unsafe-inline' 'unsafe-eval'";
|
||||
return [
|
||||
"default-src 'self'",
|
||||
`script-src ${scriptSrc}`,
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data:",
|
||||
"font-src 'self'",
|
||||
"connect-src 'self'",
|
||||
"worker-src 'self'",
|
||||
"manifest-src 'self'",
|
||||
"object-src 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
].join("; ");
|
||||
}
|
||||
|
||||
export default auth((req) => {
|
||||
const { pathname } = req.nextUrl;
|
||||
const isProtected = PROTECTED_PREFIXES.some(
|
||||
(prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`),
|
||||
);
|
||||
const csp = buildCsp();
|
||||
|
||||
if (isProtected && !req.auth) {
|
||||
const signInUrl = new URL("/sign-in", req.nextUrl.origin);
|
||||
signInUrl.searchParams.set("callbackUrl", pathname);
|
||||
return NextResponse.redirect(signInUrl);
|
||||
const response = NextResponse.redirect(signInUrl);
|
||||
response.headers.set("Content-Security-Policy", csp);
|
||||
return response;
|
||||
}
|
||||
|
||||
const response = NextResponse.next();
|
||||
response.headers.set("Content-Security-Policy", csp);
|
||||
return response;
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
Reference in new issue
Block a user