Introduction
Role-based authentication is a common requirement in modern web applications. It ensures that users can access only those pages or features meant for their role, such as admin, editor, or normal user. Next.js makes this process much simpler with the help of middleware. Middleware allows you to intercept requests before they reach a protected page and perform checks like verifying authentication and validating user roles. In this article, we will learn how to implement role-based authentication in Next.js using middleware in simple words and step-by-step instructions.
What Is Middleware in Next.js?
Middleware in Next.js runs before a request is processed. Think of it as a checkpoint that decides whether the user should continue to the page or be redirected elsewhere.
Key uses
Checking if a user is logged in
Verifying user roles (admin, editor, user)
Blocking unauthorized access
Redirecting users based on permissions
Middleware runs at the edge runtime, making it fast and efficient for authentication.
Why Use Role-Based Authentication?
Role-based authentication (RBAC) helps applications control access based on user roles. It makes your Next.js application more secure and prevents users from viewing restricted pages.
Examples
Only admins can access admin dashboard
Only editors can edit content
Regular users can access normal pages
This system helps ensure proper control and improves security.
Creating a Sample Authentication Flow
To implement role-based authentication, you will need:
User login system (JWT or cookies)
User roles stored inside the token
Middleware to read the role from the token
Common format of a decoded JWT:
{
"id": "123",
"name": "John",
"role": "admin"
}
The role field is the key part for RBAC.
Step 1: Create Middleware File in Next.js
Next.js automatically detects middleware when you create a middleware.js file in the root or inside the src folder.
Example folder structure
src/
middleware.js
app/
admin/
dashboard/
page.js
Step 2: Define Protected Routes
You must define which routes require role-based access. For example:
export const config = {
matcher: ["/admin/:path*", "/dashboard/:path*"]
};
This tells Next.js to run middleware for these routes.
Step 3: Read Token Inside Middleware
Here is a basic example of reading JWT data from cookies inside middleware:
import { NextResponse } from "next/server";
import { jwtVerify } from "jose";
export async function middleware(req) {
const token = req.cookies.get("authToken")?.value;
if (!token) {
return NextResponse.redirect(new URL("/login", req.url));
}
try {
const { payload } = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET));
req.user = payload;
} catch (error) {
return NextResponse.redirect(new URL("/login", req.url));
}
}
This middleware checks:
If token exists
If token is valid
Extracts user details
Step 4: Add Role-Based Route Protection
Now add logic to allow only specific roles to visit certain pages.
Example
export async function middleware(req) {
const token = req.cookies.get("authToken")?.value;
const url = req.nextUrl.clone();
if (!token) {
url.pathname = "/login";
return NextResponse.redirect(url);
}
const { payload } = await jwtVerify(token, new TextEncoder().encode(process.env.JWT_SECRET));
const role = payload.role;
if (req.nextUrl.pathname.startsWith("/admin") && role !== "admin") {
url.pathname = "/unauthorized";
return NextResponse.redirect(url);
}
if (req.nextUrl.pathname.startsWith("/dashboard") && !["admin", "editor"].includes(role)) {
url.pathname = "/unauthorized";
return NextResponse.redirect(url);
}
return NextResponse.next();
}
This logic ensures:
Only admin can access /admin
Admin or editor can access /dashboard
Others are redirected to /unauthorized page
Step 5: Create Unauthorized Page
Create a simple page for unauthorized users.
Example
export default function Unauthorized() {
return (
<div>
<h1>Access Denied</h1>
<p>You do not have permission to view this page.</p>
</div>
);
}
This improves user experience and avoids confusion.
Step 6: Storing Roles in the Token
When users log in, add a role field in the JWT.
Example login code
import jwt from "jsonwebtoken";
export function loginUser(user) {
const token = jwt.sign(
{
id: user.id,
name: user.name,
role: user.role
},
process.env.JWT_SECRET,
{ expiresIn: "1d" }
);
return token;
}
This allows middleware to verify permissions.
Step 7: Testing Role-Based Authentication
To test the setup:
Log in with a normal user → try accessing /admin
Log in as admin → access allowed
Log in as editor → dashboard works, admin does not
This confirms RBAC is working correctly.
Best Practices for Secure Role-Based Authentication
Always store JWT secrets in environment variables
Never expose role permission logic on the frontend
Use HTTPS for secure cookie transfer
Keep user roles simple and organized
Create a fallback page for unauthorized access
Use short token expiry for extra security
Summary
Role-based authentication in Next.js using middleware is a clean, fast, and secure way to restrict access based on user roles. By verifying tokens and checking user permissions before a request reaches the page, you can protect sensitive routes like admin dashboards and internal dashboards. With middleware, JWT roles, and structured route permissions, your Next.js application becomes highly secure, scalable, and easier to maintain.