Add Next.js middleware for route protection (task 5.1)

- Create src/middleware.ts using Auth.js v5 auth() wrapper
- Protect /app/* routes: redirect unauthenticated users to /login
- Protect /api/* routes (except /api/auth/* and /api/health): return 401 JSON for unauthenticated requests
- Redirect authenticated users away from /login and /register to /app
- Mark task 5.1 as complete in tasks.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marko Djordjevic 2026-02-20 10:18:15 +01:00
parent 10e4ec8648
commit 55fd91ff52
2 changed files with 45 additions and 1 deletions

View file

@ -26,7 +26,7 @@
## 5. Auth Middleware & Helpers ## 5. Auth Middleware & Helpers
- [ ] 5.1 `[sonnet]` Create `proxy.ts` at project root: protect `/app/*` routes (redirect to `/login`), protect `/api/*` except `/api/auth/*` and `/api/health` (return 401), redirect authenticated users from `/login` and `/register` to `/app` - [x] 5.1 `[sonnet]` Create `proxy.ts` at project root: protect `/app/*` routes (redirect to `/login`), protect `/api/*` except `/api/auth/*` and `/api/health` (return 401), redirect authenticated users from `/login` and `/register` to `/app`
- [ ] 5.2 `[haiku]` Create `src/lib/auth.ts` with `getAuthUser()` helper that extracts user from Auth.js session - [ ] 5.2 `[haiku]` Create `src/lib/auth.ts` with `getAuthUser()` helper that extracts user from Auth.js session
## 6. User Settings API ## 6. User Settings API

44
src/middleware.ts Normal file
View file

@ -0,0 +1,44 @@
import { auth } from "@/auth";
import { NextResponse } from "next/server";
export const middleware = auth((req) => {
const { pathname } = req.nextUrl;
const isAuthenticated = !!req.auth;
// Protect /api/* except /api/auth/* and /api/health
if (pathname.startsWith("/api/")) {
const isAuthRoute = pathname.startsWith("/api/auth/");
const isHealthRoute = pathname === "/api/health";
if (!isAuthRoute && !isHealthRoute && !isAuthenticated) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.next();
}
// Redirect authenticated users away from /login and /register
if (isAuthenticated && (pathname === "/login" || pathname === "/register")) {
return NextResponse.redirect(new URL("/app", req.nextUrl.origin));
}
// Protect /app/* routes — redirect unauthenticated users to /login
if (pathname.startsWith("/app") && !isAuthenticated) {
return NextResponse.redirect(new URL("/login", req.nextUrl.origin));
}
return NextResponse.next();
});
export const config = {
matcher: [
/*
* Match all request paths except:
* - _next/static (static files)
* - _next/image (image optimisation)
* - favicon.ico / favicon.png / favicon.svg
* - public assets (top-level files that are not pages)
*/
"/((?!_next/static|_next/image|favicon\\.ico|favicon\\.png|favicon\\.svg).*)",
],
};