-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmiddleware.ts
More file actions
102 lines (88 loc) · 3.04 KB
/
middleware.ts
File metadata and controls
102 lines (88 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { verifyToken } from "@/lib/jwt";
import { isProduction } from "@/lib/config";
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Public paths that don't require authentication
const publicPaths = [
"/",
"/login",
"/api/auth/login",
"/api/auth/callback",
"/api/mock",
"/api/activities",
"/callback",
];
// Check if the current path is public
const isPublicPath = publicPaths.some((path) => {
if (path === "/") {
return pathname === "/";
}
return pathname.startsWith(path);
});
// Allow public paths, Next.js internals, and public static assets
if (
isPublicPath ||
pathname.startsWith("/_next") ||
pathname.startsWith("/static") ||
/\.(png|jpg|jpeg|gif|svg|ico|webp)$/i.test(pathname)
) {
return addSecurityHeaders(NextResponse.next());
}
// Get token from cookie
const token = request.cookies.get("service_token")?.value;
// If no token and trying to access protected route, redirect to login
if (!token && !isPublicPath) {
const loginUrl = new URL("/login", request.url);
loginUrl.searchParams.set("redirect", pathname);
return NextResponse.redirect(loginUrl);
}
// Admin routes require authentication (admin permission check moved to server-side routes)
if (pathname.startsWith("/admin")) {
if (!token) {
const loginUrl = new URL("/login", request.url);
loginUrl.searchParams.set("redirect", pathname);
return NextResponse.redirect(loginUrl);
}
// Verify token using auth function
const decoded = await verifyToken(token);
if (!decoded) {
// Invalid token, redirect to login
const loginUrl = new URL("/login", request.url);
loginUrl.searchParams.set("redirect", pathname);
loginUrl.searchParams.set("error", "invalid_token");
return NextResponse.redirect(loginUrl);
}
// Admin permission check is now handled in individual admin API routes and pages
// This allows us to avoid Edge runtime limitations with fs/csv reading
}
return addSecurityHeaders(NextResponse.next());
}
function addSecurityHeaders(response: NextResponse): NextResponse {
// Add security headers
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("X-XSS-Protection", "1; mode=block");
response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
// HSTS header for production
if (isProduction()) {
response.headers.set(
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains"
);
}
return response;
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api/auth (auth endpoints)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
"/((?!_next/static|_next/image|favicon.ico).*)",
],
};