Protecting Private APIs and Pages in Next.js 13

Mustafa DalgaMustafa Dalga
3 min read

Next.js has always been a favorite for its simplicity and powerful features. With the release of Next.js 13, the framework introduced a new way to handle middleware, making it even easier to add protection layers to your application. In this post, I'll walk you through how I manage and protect private APIs and pages in my Next.js 13 project using global middleware.

1. The Importance of JWT and HttpOnly Cookies

Before diving into the middleware, it's essential to understand the authentication mechanism I use. JSON Web Tokens (JWT) are a compact, URL-safe means of representing claims to be transferred between two parties. In my setup, I store the JWT in a secure HttpOnly cookie. This approach ensures that the token cannot be accessed via JavaScript on the client side, adding an extra layer of security against potential XSS attacks.

2. Setting Up Global Middleware

Next.js 13 allows you to define global middleware that runs before every request. This is the perfect place to add our authentication checks.

Here's the middleware in its entirety:

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { CookieKeys } from "@/_enums";

const authenticatedRoutes = [
    "/dashboard",
    "/profile",
    "/settings",
    "/orders",
    "/inbox",
    "/favorites",
];
const authenticatedApis = [
    "/api/user",
    "/api/orders",
    "/api/profile",
    "/api/settings",
    "/api/messages",
    "/api/favorites",
];

export function middleware(request: NextRequest) {
    const isAuthenticated: boolean = !!(request.cookies.get(CookieKeys.Jwt)?.value);
    const pathName = request.nextUrl.pathname;

    if (!isAuthenticated) {
        if (authenticatedRoutes.includes(pathName)) {
            const url = new URL('/', request.url);
            return NextResponse.redirect(url.toString());
        }
        if (authenticatedApis.includes(pathName)) {
            return NextResponse.json({ message: "You are not authorized to access this resource." }, { status: 403 });
        }
    }

    return NextResponse.next();
}

3. How the Middleware Works

  • Authenticated Routes & APIs: I maintain two arrays, one for private pages and another for private API endpoints. Any request to these routes or endpoints will need to pass the authentication check.

  • Middleware Function: The middleware function checks if the JWT exists in the cookies. If it doesn't, it determines if the request is for a private page or a private API and takes the appropriate action.

4. Handling Unauthenticated Requests

For unauthenticated requests:

  • Private Pages: Users are redirected to the homepage (or a login page if you have one). This ensures that only authenticated users can access private content.

  • Private APIs: Instead of a redirect, the server responds with a 403 (Forbidden) status and a user-friendly message. This approach provides clear feedback to the client or developer about the authentication requirement.

5. Advantages of This Approach

  • Simplicity: The middleware is concise and easy to understand. It provides a centralized place for authentication checks, ensuring consistency across the application.

  • Flexibility: By maintaining arrays of private routes and APIs, it's straightforward to add or remove protected endpoints. This modular approach means less room for error and easier maintenance.

  • Security: Using JWTs with HttpOnly cookies ensures that even if there's an XSS vulnerability, attackers cannot access the JWT via JavaScript.

Conclusion

Protecting private content is crucial for any web application. With Next.js 13's new middleware capabilities, it's never been easier to add robust protection layers to your project. By combining JWTs, HttpOnly cookies, and global middleware, you can ensure that your private pages and APIs remain secure and accessible only to authenticated users.

0
Subscribe to my newsletter

Read articles from Mustafa Dalga directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mustafa Dalga
Mustafa Dalga

I am a highly qualified software developer with over 6 years of experience in developing desktop and web applications using various technologies.My main programming language is JavaScript, and I am currently working as a Front-end developer at Heybooster. I am passionate about software development and constantly seek to improve my skills and knowledge through research and self-teaching. I share my projects on Github as open source to not only learn and grow, but also contribute to the development community. As a software developer, I am committed to upholding ethical standards in my work.