How to get Session in NextJS Middleware with NextAuth.js
Jasser Mark Arioste
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.
The demo is available here: https://next-daisyui-starter-1m84kgrtb-jmarioste.vercel.app/admin
If the user is not authenticated, it will redirect to the /sign-in
page with a callbackURL:
If the user, is not an admin it shows the 403
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.