Handling Authentication and Redirection in Next.js Middleware

Meenu Matharu
5 min readSep 9, 2024

--

Handling Authentication and Redirection in Next.js Middleware

When developing web applications, managing user authentication and access control is critical to providing a secure and seamless experience. With Next.js, you can use middleware to intercept incoming requests and handle various scenarios, such as checking if a user is logged in, redirecting them to appropriate pages, or managing access based on user roles.

In this article, we’ll explore a middleware.ts file from a Next.js application . We will also provide examples and explanations to illustrate the use cases covered by this middleware.

The Middleware Code

Here’s the full middleware code we’ll be working with:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-url', request.nextUrl.origin);
const currentUser = request.cookies.get('accessToken')?.value;
const sessionData = request.cookies.get('session')?.value;
const copilotFlag = request.nextUrl.searchParams.get('copilot');
console.log("****************************");
console.log("sessionData:", sessionData);
console.log("currentUser----", (currentUser ? "Logged In" : "Guest User"));
console.log("request.url:", request.url);
console.log("request.nextUrl.pathname:", request.nextUrl.pathname);
if (!currentUser && request.nextUrl.pathname !== '/') {
return NextResponse.redirect(new URL('/', request.url));
}
if (copilotFlag && copilotFlag === 'true' && currentUser && request.nextUrl.pathname === '/') {
return NextResponse.redirect(new URL(`/auth=${currentUser}`, request.url));
}
if (currentUser && request.nextUrl.pathname === '/') {
return NextResponse.redirect(new URL('/dashboard-v2', request.url));
}
if (currentUser && request.nextUrl.pathname === '/dashboard') {
return NextResponse.redirect(new URL('/dashboard-v2', request.url));
}
if (
currentUser &&
(request.nextUrl.pathname === '/dashboard/logs' ||
request.nextUrl.pathname === '/dashboard/admin' ||
request.nextUrl.pathname === '/settings')
) {
const sessionObj = sessionData ? JSON.parse(sessionData) : "";
if (!sessionObj || (sessionObj && !sessionObj.roles.includes("admin"))) {
return NextResponse.redirect(new URL('/dashboard-v2', request.url));
}
}
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
}
export const config = {
matcher: ['/', '/webchat', '/dashboard', '/dashboard/:path*', '/dashboard-v2', '/settings'],
}

Explanation of the Middleware Logic

Let’s go through each section of the middleware to understand the different scenarios it handles:

1. Setting Custom Headers

Before handling any specific logic, the middleware sets a custom header (x-url) with the origin of the request. This can be useful for tracking purposes or to pass along necessary data for downstream processes.

const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-url', request.nextUrl.origin);

2. Extracting Authentication and Session Data

The middleware checks for cookies that store the user’s authentication token (accessToken) and session information (session). It also checks for a query parameter (copilot) to handle specific scenarios .

const currentUser = request.cookies.get('accessToken')?.value;
const sessionData = request.cookies.get('session')?.value;
const copilotFlag = request.nextUrl.searchParams.get('copilot');

3. Scenario 1: Redirect Unauthenticated Users

If a user is not logged in (!currentUser) and tries to access any route other than the homepage (/), they are redirected to the homepage.

if (!currentUser && request.nextUrl.pathname !== '/') {
return NextResponse.redirect(new URL('/', request.url));
}
  • Example: If a guest user tries to access /dashboard, they will be redirected to /.

4. Scenario 2: Microsoft Teams Integration Check

If the request comes from url with copilotFlag as query parameter being set to true, and the user is logged in (currentUser exists), and the request is for the homepage (/), the user is redirected to a special URL (/auth={currentUser}).

if (copilotFlag && copilotFlag === 'true' && currentUser && request.nextUrl.pathname === '/') {
return NextResponse.redirect(new URL(`/auth=${currentUser}`, request.url));
}
  • Example: A logged-in user accessing the app from url with some query parameter, will be redirected to /auth={currentUser}.

5. Scenario 3: Redirect Authenticated Users from Homepage

If a user is logged in (currentUser) and attempts to access the homepage (/), they are redirected to the new dashboard (/dashboard-v2).

if (currentUser && request.nextUrl.pathname === '/') {
return NextResponse.redirect(new URL('/dashboard-v2', request.url));
}
  • Example: A logged-in user visiting the homepage will be automatically redirected to /dashboard-v2.

6. Scenario 4: Redirect from Old to New Dashboard

If a logged-in user attempts to access the old dashboard (/dashboard), they are redirected to the new dashboard (/dashboard-v2).

if (currentUser && request.nextUrl.pathname === '/dashboard') {
return NextResponse.redirect(new URL('/dashboard-v2', request.url));
}
  • Example: A logged-in user accessing /dashboard will be redirected to /dashboard-v2.

7. Scenario 5: Role-Based Access Control

For certain paths (/dashboard/logs, /dashboard/admin, /settings), the middleware checks if the user has the admin role. If not, the user is redirected to /dashboard-v2.

if (
currentUser &&
(request.nextUrl.pathname === '/dashboard/logs' ||
request.nextUrl.pathname === '/dashboard/admin' ||
request.nextUrl.pathname === '/settings')
) {
const sessionObj = sessionData ? JSON.parse(sessionData) : "";
if (!sessionObj || (sessionObj && !sessionObj.roles.includes("admin"))) {
return NextResponse.redirect(new URL('/dashboard-v2', request.url));
}
}
  • Example: A logged-in user without the admin role trying to access /settings will be redirected to /dashboard-v2.

8. Proceed with the Request

If none of the above conditions are met, the request is allowed to continue with any modified headers.

return NextResponse.next({
request: {
headers: requestHeaders,
},
});

More Examples of Middleware Scenarios

Here are some additional scenarios you might want to consider for your middleware:

  1. Redirect Based on User Preferences: If you store user preferences (e.g., language or theme) in cookies or session data, you can use the middleware to redirect users to a localized or theme-specific version of your app.
  2. Redirect Based on User Plan: If your application has different user plans (e.g., free, pro, enterprise), you can use the middleware to manage access to features or routes based on the user’s plan.
  3. Redirect for Maintenance: If your application or specific routes are undergoing maintenance, you can use the middleware to redirect all traffic to a maintenance page.

Explanation of the matcher Configuration

The matcher property in the middleware configuration specifies the routes where the middleware should run. It is an array of strings representing the paths that will trigger the middleware:

export const config = {
matcher: ['/', '/webchat', '/dashboard', '/dashboard/:path*', '/dashboard-v2', '/settings'],
}
  • '/': Matches the homepage (/).
  • '/webchat': Matches the /webchat route.
  • '/dashboard': Matches the /dashboard route.
  • '/dashboard/:path*': Matches all routes under /dashboard (e.g., /dashboard/logs, /dashboard/admin).
  • '/dashboard-v2': Matches the /dashboard-v2 route.
  • '/settings': Matches the /settings route.

The matcher helps optimize middleware performance by running it only on specified routes, improving control and security by targeting specific paths.

Conclusion

Middleware in Next.js provides a powerful mechanism to control access and redirection at the edge. By utilizing middleware, you can handle various scenarios, such as authentication checks, role-based access control, and custom redirections based on user context. This example has demonstrated how to handle common scenarios in a Next.js application to provide a secure and user-friendly experience.

Feel free to expand upon this middleware logic to fit the unique needs of your application!

--

--

Meenu Matharu
Meenu Matharu

Written by Meenu Matharu

🚀 Passionate Frontend Developer | Storyteller on a Coding Journey 🌟 Dive deep into the world of frontend technologies like HTML, CSS, JavaScript and React

No responses yet