Search code examples
reactjsnext.jsmiddlewarenext.js13next-auth

Next-Auth getSession is returning null in my middleware.js


Basically, I am trying to create middleware in my next js project, I have a fully functional login system using next auth, and now I want to redirect users to different dashboard pages based on their role after logging in. For example, if the user role is admin then the user should be redirected to the /admin page and if the user role is donor then the user should be redirected to the /donor page and I want to do it using middleware.js,

However, I am unable to get the session to extract the user role present in the session. It returns a null value. but I can get the session in normal react components like the one below

const {data: session} = useSession()
const role = session?.user?.role

But I am unable to get the session using getSession() in the middleware.js
Note: useSession() does not work for middleware

export { default } from "next-auth/middleware";
import { NextResponse } from "next/server";
import { getSession } from "next-auth/react";

export const config = {
  matcher: ["/admin/:path*", "/donor/:path*"],
};

// Define the paths for different roles
const roleRedirects = {
  admin: "/admin",
  donor: "/donor",
};

export async function middleware(request) {
  const session = await getSession({request});
  const role = session?.user?.role;

  console.log(session); // getting null value

  if (session && role && roleRedirects[role]) {
    return NextResponse.redirect(roleRedirects[role]);
  }

  return NextResponse.next();
}

for better understanding I have added my [...nextauth].js code also

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import { connectDB } from "@/backend/utils/dbConnect";
import prisma from "@/prisma/prismaClient";

export const handler = NextAuth({
  providers: [
    CredentialsProvider({
      name: "credentials",
      credentials: {},

      async authorize(credentials, req) {
        const { identifier, password } = credentials;

        try {
          await connectDB();
          const user = await prisma.users.findFirst({
            where: {
              OR: [{ email: identifier }, { phone: identifier }],
            },
          });

          if (user !== null) {
            const passwordMatching = await bcrypt.compare(
              password,
              user.password
            );

            if (passwordMatching !== false) {
              return user;
            } else {
              return null;
            }
          } else {
            return null;
          }
        } catch (error) {
          return {
            apiMessage: {
              errorMsg: "Unexpected Error occurred ",
              error: error,
            },
          };
        }
      },
    }),
  ],

  //   session
  session: {
    strategy: "jwt",
    maxAge: 7 * 24 * 60 * 60, // Set the session max age 7 days
    expires: 30 * 24 * 60 * 60, // set the session expiration time 30 days
  },

  callbacks: {
    async jwt({ token, user }) {
      user && (token.user = user);
      return token;
    },

    async session({ session, token }) {
      session.user.id = token.user.id;
      session.user.role = token.user.role;
      delete session?.user?.email;
      delete session?.user?.phone;
      delete session?.user?.password;
      return session;
    },
  },

  pages: {
    signIn: "/login",
  },

  secret: process.env.NEXTAUTH_SECRET,
});

export { handler as GET, handler as POST };


Solution

  • I have resolved it, I have used getToken() in the middleware to get the session Here is the middleware.js file of NextJs

    import { NextResponse } from "next/server";
    import { getToken } from "next-auth/jwt";
    
    // Protected routes for each role
    const respectiveRoutes = {
      admin: "/admin",
      donor: "/donor",
    };
    
    // specify pages for middleware
    export const config = {
      matcher: ["/admin/:path*", "/donor/:path*", "/login", "/signup"],
    };
    
    export async function middleware(request) {
      const { nextUrl } = request;
    
      // Get session and extract user role
      const session = await getToken({
        req: request,
        secret: process.env.NEXTAUTH_SECRET,
      });
      const userRole = session?.user?.role.toLowerCase(); // get the user role from session
    
      // Redirect logged in user's to their respective dashboard
      if (session) {
        const rolePath = respectiveRoutes[userRole];
    
        if (nextUrl.pathname.startsWith(rolePath)) {
          return NextResponse.next();
        } else {
          // if users try to visit anauthorised dashboard, they will be redirected to their own dashboard
          for (const role in respectiveRoutes) {
            if (role !== userRole && nextUrl.pathname.startsWith(respectiveRoutes[role])) {
              //
              return NextResponse.redirect(`${process.env.NEXTAUTH_URL}${rolePath}`);
            }
          }
        }
    
        // prevent loggedin user to visit login and signup page
        if (nextUrl.pathname == "/login" || nextUrl.pathname == "/signup") {
          return NextResponse.redirect(`${process.env.NEXTAUTH_URL}${rolePath}`);
        }
      } else {
        // Redirect logged out users to login page is they want to visit protected routes
        if (["/admin", "/donor"].some((rolePath) => nextUrl.pathname.startsWith(rolePath))) {
          return NextResponse.redirect(`${process.env.NEXTAUTH_URL}/login`);
        }
      }
    
      // Allow other requests to proceed
      return NextResponse.next();
    }