diff --git a/dashboards/admin-web/src/middleware.ts b/dashboards/admin-web/src/middleware.ts new file mode 100644 index 00000000..4b950865 --- /dev/null +++ b/dashboards/admin-web/src/middleware.ts @@ -0,0 +1,52 @@ +import { NextRequest, NextResponse } from 'next/server'; + +/** + * Edge middleware — lightweight JWT presence check on all /api/* routes. + * Does NOT verify the token (that happens in route handlers via requireAdmin/getCurrentUser). + * Blocks completely unauthenticated requests before they reach API logic. + */ + +const PUBLIC_PATHS = new Set([ + '/api/auth/login', + '/api/auth/forgot-password', + '/api/health', + '/api/seed', +]); + +function isPublic(pathname: string): boolean { + return PUBLIC_PATHS.has(pathname); +} + +function getToken(req: NextRequest): string | null { + // Cookie: token=... + const cookie = req.cookies.get('token'); + if (cookie?.value) return cookie.value; + + // Authorization: Bearer ... + const authHeader = req.headers.get('authorization'); + if (authHeader?.startsWith('Bearer ')) return authHeader.slice(7); + + return null; +} + +export function middleware(req: NextRequest) { + const { pathname } = req.nextUrl; + + // Only protect /api/* routes (skip pages, static assets, etc.) + if (!pathname.startsWith('/api/')) return NextResponse.next(); + + // Allow public routes + if (isPublic(pathname)) return NextResponse.next(); + + // Require token presence + const token = getToken(req); + if (!token) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + return NextResponse.next(); +} + +export const config = { + matcher: '/api/:path*', +};