DEV Community

Cover image for Authentication with middlewares in NextJS
Super
Super

Posted on

Authentication with middlewares in NextJS

Overview

Hey there! Today, we're going to talk about authentication in Next.js using middlewares. If you're not familiar, Next.js is a really cool framework for building server-rendered React applications.

So, what's authentication all about? Well, it's a way to make sure that only the right people can access certain parts of your web app. And that's where middlewares come in handy! They're like helpers that can do certain things before requests reach the actual route handlers.

This article will discuss the fundamentals of middlewares and demonstrate how they can be utilized in a Next.js application to establish a secure authentication system. Upon reading this article, you'll acquire a comprehensive understanding of how to utilize middlewares for authentication in Next.js and have the ability to enhance the security of your web apps. Let's begin and explore the world of authentication with Next.js.

Set up

Packages

First, we have to have a NextJS project, then install Axios for better HTTP Handling

yarn add axios
Enter fullscreen mode Exit fullscreen mode

Services

Next we have to define a service, in this case AuthService to handle authentication:

import axios, { AxiosInstance } from "axios";

export class AuthService {
  protected readonly instance: AxiosInstance;
  public constructor(url: string) {
    this.instance = axios.create({
      baseURL: url,
      timeout: 30000,
      timeoutErrorMessage: "Time out!",
    });
  }

  login = (username: string, password: string) => {
    return this.instance
      .post("/login", {
        username,
        password,
      })
      .then((res) => {
        return {
          username: res.data.username,
          avatar: res.data.avatar,
          id: res.data.userId,
          accessToken: res.data.access_token,
          expiredAt: res.data.expiredAt,
        };
      });
  };
}
Enter fullscreen mode Exit fullscreen mode

This code defines a class named AuthService that exports it as a module. The AuthService class has a constructor that takes a URL as a parameter to create an Axios instance based on that URL. Axios is a popular JavaScript library used to make HTTP requests from a web browser or Node.js.

The login method is defined within the AuthService class, which takes two parameters, username and password. This method sends a POST request to the /login endpoint of the API service using the Axios instance created in the constructor. The username and password are passed as an object in the POST request body.

If the POST request is successful, the method returns an object that contains the username, avatar, id, accessToken, and expiredAt properties. These properties are extracted from the response data, which is received from the API service.

Overall, this code defines a reusable AuthService class that encapsulates the logic for making an HTTP request to an API service to log in a user and retrieves the user's information.

Routes

export const protectedRoutes = ["/profile"];
export const authRoutes = ["/login"];
export const publicRoutes = ["/about", "/"];
Enter fullscreen mode Exit fullscreen mode

We need 3 types of routes:

  • Protected routes: only logged-in users can access these routes
  • Auth routes: routes that serve authentication, authenticated users can't access these routes
  • Public routes: routes that can be accessed by anyone

Login hook

We need a hook to handle login function:

import { authService } from "../../services";
import Cookies from "js-cookie";
import { User } from "../../types/user";

export const useLogin = () => {
  const login = async (username: string, password: string) => {
    const user = await authService.login(username, password);
    if (user) {
      Cookies.set("currentUser", JSON.stringify(user));
    }
    return user as User;
  };

  return { login };
};
Enter fullscreen mode Exit fullscreen mode

This code exports a function named useLogin that returns an object with the login function. The login function takes two parameters: username and password.

Within the login function, an asynchronous call is made to authService.login(username, password), which is a method defined in another module, authService. This method returns a promise that resolves to a User object.

If the User object is truthy, meaning it exists, the Cookies.set method is called to set a cookie named "currentUser" with the stringified User object as its value.

Finally, the login function returns the User object as a type assertion to inform TypeScript that the returned value is indeed a User.

Middleware

This is the most important part of the article, using built-in middleware.ts of NextJS:

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { authRoutes, protectedRoutes } from "./src/router/routes";

export function middleware(request: NextRequest) {
  const currentUser = request.cookies.get("currentUser")?.value;

  if (
    protectedRoutes.includes(request.nextUrl.pathname) &&
    (!currentUser || Date.now() > JSON.parse(currentUser).expiredAt)
  ) {
    request.cookies.delete("currentUser");
    const response = NextResponse.redirect(new URL("/login", request.url));
    response.cookies.delete("currentUser");

    return response;
  }

  if (authRoutes.includes(request.nextUrl.pathname) && currentUser) {
    return NextResponse.redirect(new URL("/profile", request.url));
  }
}
Enter fullscreen mode Exit fullscreen mode

This code defines a middleware function that is used in a Next.js application. Middlewares are functions that can modify incoming requests and outgoing responses in a web application.

The middleware function takes a NextRequest object as its parameter, which is provided by the Next.js server. The NextRequest object represents an incoming request and contains information about the URL, headers, cookies, and other request-specific data.

The middleware function first retrieves the currentUser cookie from the request using request.cookies.get("currentUser")?.value.

If the requested URL is a protected route (defined in protectedRoutes), and the currentUser cookie is not present or has expired, the middleware will delete the currentUser cookie and redirect the user to the login page using NextResponse.redirect(new URL("/login", request.url)). The response.cookies.delete("currentUser") statement removes the currentUser cookie from the response object.

If the requested URL is an authentication route (defined in authRoutes) and the currentUser cookie is present, the middleware will redirect the user to the profile page using NextResponse.redirect(new URL("/profile", request.url)).

Conclusion

In conclusion, this article has covered the fundamentals of authentication using middlewares in Next.js. We've discussed how middlewares can be used to manage authentication in web applications by intercepting incoming requests and performing necessary actions before they reach the actual route handlers. We've also explored how to implement a secure authentication system using middlewares in a Next.js application.

If you find this article is too confusing, you can check out the source code: https://github.com/superdev163/nextjs-auth

Top comments (4)

Collapse
 
diwakarkashyap profile image
DIWAKARKASHYAP • Edited

write "javascript" keyword after triple quotes , it will beautify the code like that

import axios, { AxiosInstance } from "axios";

export class AuthService {
  protected readonly instance: AxiosInstance;
  public constructor(url: string) {
    this.instance = axios.create({
      baseURL: url,
      timeout: 30000,
      timeoutErrorMessage: "Time out!",
    });
  }

  login = (username: string, password: string) => {
    return this.instance
      .post("/login", {
        username,
        password,
      })
      .then((res) => {
        return {
          username: res.data.username,
          avatar: res.data.avatar,
          id: res.data.userId,
          accessToken: res.data.access_token,
          expiredAt: res.data.expiredAt,
        };
      });
  };
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
brainiacneit profile image
Super

thanks

Collapse
 
practicaldev2024 profile image
Inzamam Baig

Where am I supposed to put this middleware file? In root or in each route directory?

For example, I have two different user roles in my application:

client
admin

Collapse
 
its_me_time_yo profile image
Its_me

You need to create this middleware file in the root directory of the next project