Introduction
Role-Based Access Control (RBAC) allows you to manage user permissions and restrict access to different parts of your application based on user roles. Implementing RBAC in a Next.js application involves managing roles, protecting routes, and controlling access. This guide walks you through setting up RBAC in a Next.js application.
Overview
RBAC in Next.js typically involves.
- Defining Roles: Setting up user roles in your application.
- Assigning Roles: Assigning roles to users during registration or profile updates.
- Protecting Routes: Restricting access to pages based on roles.
Setting Up Roles
Define Roles in Your Database
Store roles in your database schema. For example, with a MongoDB schema.
// models/User.js
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
email: String,
password: String,
role: {
type: String,
enum: ['user', 'admin', 'moderator'],
default: 'user'
}
});
export default mongoose.models.User || mongoose.model('User', userSchema);
Assign Roles During Registration
When registering a user, assign a role based on your requirements.
// pages/api/register.js
import User from '../../models/User';
import dbConnect from '../../lib/dbConnect';
export default async function handler(req, res) {
if (req.method === 'POST') {
await dbConnect();
const { email, password, role } = req.body;
// Create a new user with a specified role
const user = new User({ email, password, role });
await user.save();
res.status(201).json({ message: 'User created' });
} else {
res.status(405).json({ message: 'Method not allowed' });
}
}
Protecting Routes
Create a Higher-Order Component (HOC)
A Higher-Order Component can be used to wrap pages and enforce role-based access control.
// components/withRole.js
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
const withRole = (WrappedComponent, allowedRoles) => {
return (props) => {
const [userRole, setUserRole] = useState(null);
const router = useRouter();
useEffect(() => {
const fetchUserRole = async () => {
const res = await fetch('/api/user');
const data = await res.json();
setUserRole(data.role);
if (!allowedRoles.includes(data.role)) {
router.push('/403'); // Redirect to a forbidden page
}
};
fetchUserRole();
}, [allowedRoles, router]);
if (!userRole) return <div>Loading...</div>;
return <WrappedComponent {...props} />;
};
};
export default withRole;
Protect Pages
Wrap your pages with the HOC to enforce role-based access.
// pages/admin.js
import withRole from '../components/withRole';
const AdminPage = () => (
<div>
<h1>Admin Dashboard</h1>
{/* Admin content */}
</div>
);
export default withRole(AdminPage, ['admin']);
Handling Unauthorized Access
Create a 403 Forbidden Page
Design a custom page for unauthorized access.
// pages/403.js
const Forbidden = () => (
<div>
<h1>403 - Forbidden</h1>
<p>You do not have permission to access this page.</p>
</div>
);
export default Forbidden;
Best Practices
- Secure Roles: Ensure roles and permissions are securely managed and validated on the server side.
- Clear Messaging: Provide clear feedback to users who attempt to access restricted areas.
- Regular Audits: Periodically review and update role definitions and permissions.
Summary
Implementing Role-Based Access Control in Next.js involves defining roles, assigning them to users, and protecting routes based on these roles. By using Higher-Order Components and server-side checks, you can effectively manage access and ensure that users can only access the parts of the application for which they are authorized. This approach enhances security and ensures a better user experience by controlling access based on user roles.