Guest User

Untitled

a guest
Dec 21st, 2024
17
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { NextResponse } from "next/server";
  2. import { match } from "path-to-regexp";
  3. import { jwtDecode as jwt_decode } from "jwt-decode";
  4.  
  5. import routes from "acl/routes";
  6. import clubRedirects from "acl/clubRedirects";
  7.  
  8. const redirect = (url, contentSecurityPolicyHeaderValue) => {
  9.   const redirectRes = NextResponse.redirect(url);
  10.   redirectRes.headers.set(
  11.     "Content-Security-Policy",
  12.     contentSecurityPolicyHeaderValue,
  13.   );
  14.   redirectRes.headers.set("X-Content-Type-Options", "nosniff");
  15.   redirectRes.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
  16.   return redirectRes;
  17. };
  18.  
  19. // TODO: make multiple middlewares (one for route acl, one for club redirects) and combine them
  20. export function middleware(req) {
  21.   const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
  22.   const { pathname } = req.nextUrl;
  23.   const cspHeader = `
  24.     default-src 'none';
  25.     script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https: http: 'unsafe-inline' ${
  26.       process.env.NODE_ENV === "production" ? "" : `'unsafe-eval'`
  27.     };
  28.     manifest-src 'self';
  29.     style-src 'self' 'nonce-${nonce}';
  30.     style-src-attr 'self' 'unsafe-inline';
  31.     style-src-elem 'self' 'unsafe-inline';
  32.     img-src 'self' blob: data: https://uptime.betterstack.com;
  33.     font-src 'self' data:;
  34.     object-src 'none';
  35.     base-uri 'self';
  36.     form-action 'self';
  37.     frame-src ${
  38.       pathname.includes("/docs") || process.env.NODE_ENV !== "production"
  39.         ? "http://localhost https://clubs.iiit.ac.in https://life.iiit.ac.in"
  40.         : "https://clubs.iiit.ac.in https://life.iiit.ac.in"
  41.     };
  42.     frame-ancestors 'self' https://*.iiit.ac.in https://iiit.ac.in;
  43.     connect-src 'self' https://api.iconify.design/ https://api.unisvg.com/ https://api.simplesvg.com/;
  44.     upgrade-insecure-requests;
  45.   `;
  46.  
  47.   // Replace newline characters and spaces
  48.   const contentSecurityPolicyHeaderValue = cspHeader
  49.     .replace(/\s{2,}/g, " ")
  50.     .trim();
  51.  
  52.   const requestHeaders = new Headers(req.headers);
  53.   requestHeaders.set("x-nonce", nonce);
  54.   requestHeaders.set(
  55.     "Content-Security-Policy",
  56.     contentSecurityPolicyHeaderValue,
  57.   );
  58.   requestHeaders.set("X-Content-Type-Options", "nosniff");
  59.   requestHeaders.set("Referrer-Policy", "strict-origin-when-cross-origin");
  60.  
  61.   const response = NextResponse.next({
  62.     request: {
  63.       headers: requestHeaders,
  64.     },
  65.   });
  66.   response.headers.set(
  67.     "Content-Security-Policy",
  68.     contentSecurityPolicyHeaderValue,
  69.   );
  70.   response.headers.set("X-Content-Type-Options", "nosniff");
  71.   response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
  72.  
  73.   // if logout cookie is set, log the user out
  74.   if (req.cookies.has("logout")) {
  75.     // clear logout cookie
  76.     req.cookies.delete("logout");
  77.     return redirect(
  78.       new URL("/logoutCallback", req.url),
  79.       contentSecurityPolicyHeaderValue,
  80.     );
  81.   }
  82.  
  83.   // redirect to CC about page
  84.   if (pathname === "/student-bodies/clubs") {
  85.     return redirect(
  86.       new URL("/student-bodies/clubs-council", req.url),
  87.       contentSecurityPolicyHeaderValue,
  88.     );
  89.   }
  90.  
  91.   // check if current route is protected
  92.   const protectedRoute =
  93.     Object.keys(routes).find((r) => match(r)(pathname)) || false;
  94.  
  95.   // if not, proceed to the page
  96.   if (!protectedRoute) {
  97.     return response;
  98.   }
  99.  
  100.   // if protected and current user is not logged in, redirect to login page
  101.   if (!req.cookies.has("Authorization")) {
  102.     return redirect(
  103.       new URL(`/login${pathname}`, req.url),
  104.       contentSecurityPolicyHeaderValue,
  105.     );
  106.   }
  107.  
  108.   // if logged in, extract user attributes
  109.   const token = req.cookies.get("Authorization"); // get token from request header
  110.   const user = jwt_decode(token?.value);
  111.  
  112.   // check if current route is to be redirected for club accounts
  113.   const clubRedirectRoute =
  114.     Object.keys(clubRedirects).find((r) => match(r)(pathname)) || false;
  115.  
  116.   // club account specific redirects
  117.   if (clubRedirectRoute && user?.role === "club") {
  118.     return redirect(
  119.       new URL(clubRedirects[pathname], req.url),
  120.       contentSecurityPolicyHeaderValue,
  121.     );
  122.   }
  123.  
  124.   // check if user has access to route
  125.   if (!routes[protectedRoute].includes(user?.role)) {
  126.     return redirect(new URL("/", req.url), contentSecurityPolicyHeaderValue);
  127.   }
  128.  
  129.   // continue to page
  130.   return response;
  131. }
  132.  
  133. export const config = {
  134.   matcher: [
  135.     /*
  136.      * Match all request paths except for the ones starting with:
  137.      * - api (API routes)
  138.      * - _next/static (static files)
  139.      * - _next/image (image optimization files)
  140.      * - favicon.ico (favicon file)
  141.      */
  142.     {
  143.       source: "/((?!api|_next/static|_next/image|favicon.ico).*)",
  144.       missing: [
  145.         { type: "header", key: "next-router-prefetch" },
  146.         { type: "header", key: "purpose", value: "prefetch" },
  147.       ],
  148.     },
  149.   ],
  150. };
Add Comment
Please, Sign In to add comment