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.
48 lines
2.2 KiB
TypeScript
48 lines
2.2 KiB
TypeScript
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).*)"],
|
|
};
|