ReactHustle

How to get Session in NextJS Middleware with NextAuth.js

Jasser Mark Arioste

Jasser Mark Arioste

How to get Session in NextJS Middleware with NextAuth.js

Hello hustlers! In this tutorial, you'll learn how to get the session inside NextJS middleware and NextAuthJS.

Prerequisites #

  • Must use NextJS v12.2+
  • Must use Nextauth.js v4.13+
  • We'll be using Typescript in this tutorial.

Using getToken to check the user #

To get the session inside middleware.ts, it's better to use getToken. getSession is not ideal since it's only available on the client side.

Here's a middleware that uses a role-based authorization implementation. It redirects to /sign-in page if there is no session and shows /403 if the user is not an admin.

// middleware.ts
import { getToken } from "next-auth/jwt";
import { NextRequest, NextResponse } from "next/server";
// paths that require authentication or authorization
const requireAuth: string[] = ["/admin"];
export async function middleware(request: NextRequest) {
  const res = NextResponse.next();
  const pathname = request.nextUrl.pathname;
  if (requireAuth.some((path) => pathname.startsWith(path))) {
    const token = await getToken({
      req: request,
      secret: process.env.SECRET,
    });
    //check not logged in
    if (!token) {
      const url = new URL(`/api/auth/signin`, request.url);
      url.searchParams.set("callbackUrl", encodeURI(request.url));
      return NextResponse.redirect(url);
    }
    //check if not authorized
    if (token.role !== "admin") {
      const url = new URL(`/403`, request.url);
      return NextResponse.rewrite(url);
    }
  }
  return res;
}
123456789101112131415161718192021222324252627

Explanation:

Line 5: We list the paths that require authentication and authorization

Line 9: We check if the URL requires authentication and authorization.

Line 10-13: We get the token using the getToken() function and we pass in the request and secret. Note that passing in the secret is optional if you use the environment variable NEXTAUTH_SECRET since next-auth automatically detects this. 

Lines 14+: We check the token and redirect the user or show 403 if not authorized.

Refactoring #

As a best practice, let's refactor our code. This will be useful if you have other logic in your middleware function. We'll create a higher-order function that produces a new middleware.

First, create a file `middlewares/withAuthorization.ts` and use the code below:

import { getToken } from "next-auth/jwt";
import {
  NextFetchEvent,
  NextMiddleware,
  NextRequest,
  NextResponse,
} from "next/server";

export default function withAuthorization(
  middleware: NextMiddleware,
  requireAuth: string[] = []
) {
  return async (request: NextRequest, next: NextFetchEvent) => {
    const pathname = request.nextUrl.pathname;
    if (requireAuth.some((path) => pathname.startsWith(path))) {
      const token = await getToken({
        req: request,
        secret: process.env.NEXTAUTH_SECRET,
      });
      if (!token) {
        const url = new URL(`/api/auth/signin`, request.url);
        url.searchParams.set("callbackUrl ", encodeURI(request.url));
        return NextResponse.redirect(url);
      }
      if (token.role !== "admin") {
        const url = new URL(`/403`, request.url);
        return NextResponse.rewrite(url);
      }
    }
    return middleware(request, next);
  };
}
1234567891011121314151617181920212223242526272829303132

Explanation: It returns a new middleware function that just adds logic to the old middleware function. In code design, this is called the decorator design pattern. But here we're applying it in NextJS. How to Chain Multiple Middleware Functions in NextJS

Inside your middleware.ts file, here's how to use the withAuthorization middleware:

import withAuthorization from "middlewares/withAuthorization";
import { NextMiddleware, NextResponse } from "next/server";
const mainMiddleware: NextMiddleware = (request) => {
  const res = NextResponse.next();
  //other middleware operations
  return res;
};
export default withAuthorization(mainMiddleware, ["/admin"]);
12345678

Explanation: we just pass in our other middleware and wrap it with authentication and authorization logic.

Code and Demo #

The code is available on GitHub.

If the user is not authenticated, it will redirect to the /sign-in page with a callbackURL:

Middleware redirects to sign-in page

If the user, is not an admin it shows the 403 page:

Middleware uses rewrites to show unauthorized page

Conclusion #

We learned how to get the session inside middleware in NextJS. We also learned a bit about refactoring middleware. Using middleware and nextauth.js provides a great user experience when handling these kinds of scenarios. I believe that it's better to do it this way rather than handling it on the client side.

Resources #

Related Posts #

Share this post!

Related Posts