You are currently viewing From Crafting to Safeguarding: Next.js Middleware & Server-Side Power for Role-Specific Pages

From Crafting to Safeguarding: Next.js Middleware & Server-Side Power for Role-Specific Pages

Writing for

Good

Insights

From Crafting to Safeguarding: Next.js Middleware & Server-Side Power for Role-Specific Pages

Written by:
icon

In Brief

Next.js has rapidly risen as one of the go-to React frameworks, largely thanks to its developer-friendly features and powerful built-in capabilities. One of the defining characteristics of Next.js is its hybrid nature—allowing both server-side rendering and static site generation, making it a versatile tool in a developer’s arsenal.

However, as applications scale and complexities grow, developers often find themselves grappling with a critical challenge: securing routes and content based on user roles, especially in dynamic applications where user-specific content is involved.

Utilizing Next.js’s Middleware

Before diving in, let’s understand the role of middleware. In Next.js, middleware enables you to run server-side code before your application handles the request. This presents a unique opportunity to enforce role-based redirects without even touching your main application code.

For instance, consider a web application with different dashboards for administrators and employees. When administrators log in, they should be redirected to the admin dashboard, while other roles, like employees, see their specific dashboard.

Here’s some best practices for how you can set this up with middleware:

mobile device login screen with security badge

src/middleware.ts

import { withMiddlewareAuthRequired, getSession } from '@auth0/nextjs-auth0/edge'; import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; import { decodeToken } from './api/helpers/authorization.helper'; import { hasAdminRole } from './api/helpers/user-role.helper'; export default withMiddlewareAuthRequired(async (req: NextRequest) => { const res = NextResponse.next(); const session = await getSession(req, res); if (session && session.accessToken) { const permissions = decodeToken(session.accessToken).permissions; const isAdmin = hasAdminRole(permissions); if (isAdmin && req.nextUrl.pathname === '/') { return NextResponse.rewrite(new URL('/admin-dashboard', req.url)); } } return res; });

In the above code:

  1. We retrieve the user session.
  2. Decode the access token to fetch permissions.
  3. Check if the user has the admin role and then redirect to the admin-dashboard.
 

Securing Your Pages on the Server Side

While middleware is powerful for handling redirects based on roles, the real essence of security lies in protecting your content at its source—the server.

If, for example, a non-admin user tries to enter the URL of the administrators’ dashboard, the server side will show this user the 404 error page.

This is very important because:

  1. This will prevent the user from accessing the administrators’ dashboard.
  2. The user will not even know which path is correct for this dashboard because they will be automatically redirected to the 404 error page, which could also happen if they type a URL that does not exist. Complete security!
 

For the Admin-Dashboard:

To prevent non-admin users from accessing the admin-dashboard directly by typing the URL, use server-side props to validate the role:

Server-Side

src/server/admin-dashboard/admin-dashboard.tsx

import { GetServerSidePropsContext } from 'next'; import { getAccessToken } from '@auth0/nextjs-auth0'; import { decodeToken } from '../../api/helpers/authorization.helper'; import { hasAdminRole } from '../../api/helpers/user-role.helper'; async function adminDashboardPage({ req, res }: GetServerSidePropsContext) { const accessToken = (await getAccessToken(req, res)).accessToken; if (accessToken) { const permissions = decodeToken(accessToken).permissions; const isAdmin = hasAdminRole(permissions); if (!isAdmin) { return { notFound: true, }; } } else { return { notFound: true, }; } return { props: { token: accessToken, isHomePage: true } }; } export default adminDashboardPage;

 

Calling server side props in Page

src/pages/admin-dashboard/index.tsx

import Head from 'next/head'; import { useTranslation } from 'react-i18next'; export { default as getServerSideProps } from '../../server/admin-dashboard/admin-dashboard'; const AdminDashboard = () => { const { t } = useTranslation('common'); return ( <> <Head> <title>{t('title')}</title> </Head> {/* Rest of the page */} </> ); }; export default AdminDashboard;

In this code, if the user doesn’t have an admin role or isn’t authenticated, they’ll receive a 404 Not Found response, thereby concealing the admin dashboard.

For the Employee Dashboard:

While the admin-dashboard requires validation, the employee dashboard, in this example, is assumed to be accessible to all authenticated users:

 

Server-Side

src/server/dashboard/dashboard.tsx

import { GetServerSidePropsContext } from 'next'; async function dashboardPage({ req, res }: GetServerSidePropsContext) { // No extra validation needed return { props: { isHomePage: true, }, }; } export default dashboardPage;

 

Calling server-side props in Page

src/pages/dashboard/index.tsx

import Head from 'next/head'; import { useTranslation } from 'react-i18next'; export { default as getServerSideProps } from '../../server/dashboard/dashboard'; const Dashboard = () => { const { t } = useTranslation('common'); return ( <> <Head> <title>{t('title')}</title> </Head> {/* Rest of the page */} </> ); }; export default Dashboard;

Conclusion

By harnessing the power of Next.js middleware and server-side props, developers can create secure experiences tailored to different user roles. While this guide focused on dashboards, these techniques can be applied across various scenarios to enhance user experience and security. Always prioritize security and be mindful of the implications of the logic you implement.

 Happy coding!

Leave a Reply