feat: add middleware for page route protection
middleware.ts gates page routes by default (redirect to /login when unauthenticated), except /login itself and Next.js internal asset paths - config.matcher excludes /api entirely, /_next/static, /_next/image, and favicon.ico. Default-deny: everything requires login unless explicitly excluded, including the home page, rather than an opt-in allowlist new routes could forget to join. Builds its own lightweight NextAuth instance from auth.config.ts (no Credentials provider) instead of importing auth from auth.ts, to stay Edge-Runtime compatible - see auth.config.ts. API routes are deliberately NOT gated here (config.matcher excludes /api). Auth.js guidance is to verify the session as close to the data fetching as possible rather than rely on middleware alone; API route auth is added directly in each route handler instead (next commit). This project pins Next.js 14.2.35, which predates the Next.js 16 middleware.ts -> proxy.ts rename, so this stays middleware.ts with a default export, not proxy.ts.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import NextAuth from "next-auth";
|
||||
import { NextResponse } from "next/server";
|
||||
import authConfig from "@/auth.config";
|
||||
|
||||
// Route-gating for page requests: "everything requires login by default,
|
||||
// explicit allowlist for what doesn't" (safer than an opt-in allowlist that
|
||||
// new routes can forget to add themselves to).
|
||||
//
|
||||
// This project pins Next.js 14.2.35, which predates Next.js 16's
|
||||
// middleware.ts -> proxy.ts rename (see https://nextjs.org/docs/messages/middleware-to-proxy)
|
||||
// so this stays middleware.ts with a default-exported `middleware` function,
|
||||
// not proxy.ts. Auth.js v5's docs (https://authjs.dev/getting-started/session-management/protecting)
|
||||
// confirm: older Next.js versions re-export `auth` as `middleware`; only
|
||||
// Next.js 16+ uses `proxy`.
|
||||
//
|
||||
// Deliberately builds its own NextAuth instance from src/auth.config.ts
|
||||
// (no Credentials provider) instead of importing `auth` from src/auth.ts —
|
||||
// this file runs on the Edge Runtime, and src/auth.ts's Credentials
|
||||
// provider pulls in bcryptjs (Node-only); see auth.config.ts's comment.
|
||||
// This instance only needs to decode/validate the JWT session cookie, which
|
||||
// is Edge-safe.
|
||||
//
|
||||
// config.matcher below excludes /api entirely, so this middleware never
|
||||
// runs on API routes (including /api/auth/* itself, needed for the login
|
||||
// flow) — API route authorization is handled separately, directly in each
|
||||
// route handler (see src/app/api/requests/route.ts), per Auth.js's own
|
||||
// guidance to verify the session as close to the data fetching as possible
|
||||
// rather than relying on middleware alone. That also means /login doesn't
|
||||
// need special-casing in the matcher, only in the redirect check below (to
|
||||
// avoid an infinite redirect loop), and Next.js's internal asset paths
|
||||
// (_next/static, _next/image, favicon.ico) never reach this middleware.
|
||||
const { auth } = NextAuth(authConfig);
|
||||
|
||||
export default auth((req) => {
|
||||
const isLoginPage = req.nextUrl.pathname === "/login";
|
||||
|
||||
if (!req.auth && !isLoginPage) {
|
||||
const loginUrl = new URL("/login", req.nextUrl.origin);
|
||||
return NextResponse.redirect(loginUrl);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
});
|
||||
|
||||
export const config = {
|
||||
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
||||
};
|
||||
Reference in New Issue
Block a user