// apps/admin/src/middleware.ts // D3RO Voice — Industrial Grade Admin Route & Security Guard import { NextResponse, type NextRequest } from 'next/server' const PUBLIC_PATHS = ['/login', '/auth/callback', '/api/auth/login', '/api/auth/logout', '/favicon.ico', '/robots.txt'] export async function middleware(request: NextRequest): Promise { const { pathname } = request.nextUrl // 1. Check if path is public (e.g. login, static assets) const isPublic = PUBLIC_PATHS.some((path) => pathname === path || pathname.startsWith(path + '/')) const isStatic = pathname.startsWith('/_next') || pathname.startsWith('/static') || pathname.includes('.') // 2. Validate session cookie const sessionCookie = request.cookies.get('d3ro_admin_session')?.value let isAuthenticated = false if (sessionCookie) { try { const decoded = JSON.parse(Buffer.from(sessionCookie, 'base64').toString('utf-8')) if (decoded && decoded.expiresAt && decoded.expiresAt > Date.now()) { isAuthenticated = true } } catch { isAuthenticated = false } } // 3. Unauthenticated access to protected route -> Redirect to /login if (!isAuthenticated && !isPublic && !isStatic) { const loginUrl = new URL('/login', request.url) if (pathname !== '/') { loginUrl.searchParams.set('redirect', pathname) } const redirectResponse = NextResponse.redirect(loginUrl) addSecurityHeaders(redirectResponse) return redirectResponse } // 4. Authenticated user visiting /login -> Redirect to Dashboard / if (isAuthenticated && pathname === '/login') { const dashboardUrl = new URL('/', request.url) const redirectResponse = NextResponse.redirect(dashboardUrl) addSecurityHeaders(redirectResponse) return redirectResponse } // 5. Proceed with Security Headers attached const response = NextResponse.next({ request: { headers: request.headers } }) addSecurityHeaders(response) return response } function addSecurityHeaders(response: NextResponse): void { // Anti-Crawling & Anti-Reconnaissance (Shodan, Google, Bing, AI scrapers) response.headers.set('X-Robots-Tag', 'noindex, nofollow, noarchive, nosnippet, noimageindex') // Clickjacking Prevention response.headers.set('X-Frame-Options', 'DENY') // MIME Sniffing Prevention response.headers.set('X-Content-Type-Options', 'nosniff') // Referrer Privacy response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin') // Feature Policy response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()') } export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'], }