Phase 8a Task 6. The matcher's exclusion is unchanged; this is the explicit short-circuit so a future matcher edit cannot accidentally lock the public route out.
37 lines
No EOL
1.3 KiB
TypeScript
37 lines
No EOL
1.3 KiB
TypeScript
import { NextResponse, type NextRequest } from "next/server";
|
|
|
|
// From @/lib/auth/cookie, NOT @/lib/pb/server. Middleware runs on the Edge
|
|
// runtime, where `server-only` and `next/headers` do not exist.
|
|
import { AUTH_COOKIE } from "@/lib/auth/cookie";
|
|
|
|
/**
|
|
* A coarse redirect for signed-out visitors, nothing more.
|
|
*
|
|
* It checks only that a cookie is present — it does not validate the token and
|
|
* must never be relied on for authorization. Every route handler and page
|
|
* re-checks the session itself. Middleware runs before the request reaches
|
|
* them and is the wrong place to make security decisions.
|
|
*/
|
|
export function middleware(request: NextRequest) {
|
|
const hasAuth = request.cookies.has(AUTH_COOKIE);
|
|
|
|
// Public intake links have no session. The token IS the authentication,
|
|
// and the route's own checks enforce it. A redirect here would lock every
|
|
// visitor out of the wizard, so /intake/:path* is the one path the
|
|
// cookie check is allowed to ignore.
|
|
if (request.nextUrl.pathname.startsWith("/intake/")) {
|
|
return NextResponse.next();
|
|
}
|
|
|
|
if (!hasAuth) {
|
|
const url = request.nextUrl.clone();
|
|
url.pathname = "/login";
|
|
return NextResponse.redirect(url);
|
|
}
|
|
|
|
return NextResponse.next();
|
|
}
|
|
|
|
export const config = {
|
|
matcher: ["/", "/p/:path*", "/plan", "/admin/:path*"],
|
|
}; |