Khaled El Janoudi
Khaled El Janoudi

Reputation: 1

The edge runtime does not support Node.js 'crypto' module error while adding mongoClient and trying to fetch the sesssion in Auth Js v5

My project uses Authjs for authentication, Ive followed the documentation in splitting the auth file into auth.config and pasted their middleware: https://authjs.dev/guides/edge-compatibility .

import NextAuth from "next-auth";
import authConfig from "@/app/auth.config";

export const { auth: middleware } = NextAuth(authConfig);

However I need to fetch the session on the middlware, which when I try to do, I get the error: Error: The edge runtime does not support Node.js 'crypto' module. Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime

here is my middlware:

import { NextResponse } from "next/server";
// import { jwtVerify } from "jose";

import { auth } from "@/app/api/auth";
import NextAuth from "next-auth";


export async function middleware(request) {
  // Error: The edge runtime does not support Node.js 'crypto' module.

  const session = await auth();
  console.log(session);

  return NextResponse.next();
}

// Middleware matcher configuration
export const config = {
  matcher: [
    "/dashboard/:path*",
    "/booking/:path*",
    "/",
    "/about",
    "/contact",
    "/admin/:path*",
    "/login/:path*",
    "/signup/:path*",
  ],
};

here is the authjs file:

import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
import { MongoDBAdapter } from "@auth/mongodb-adapter";
import client from "./client";
import authConfig from "../auth.config";
const db = client.db(process.env.MONGODB_DB);

// Simple function to get user role from database
// async function getUserRole(email) {
//   const user = await db.collection("users").findOne({ email });
//   return user?.role || "user";
// }

export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter: MongoDBAdapter(client),
  secret: process.env.NEXTAUTH_SECRET,
  session: {
    strategy: "jwt",
    maxAge: 30 * 24 * 60 * 60, // 30 days
  },
  ...authConfig,
  callbacks: {
    async jwt({ token, user, account }) {
      // Initial sign in
      if (account && user) {
        token.accessToken = account.access_token;
        token.refreshToken = account.refresh_token;
        token.accessTokenExpires = account.expires_at * 1000;
        token.userId = user.id;
        // token.role = user.role;
      }

      // Check if token needs refresh
      if (token.accessTokenExpires && Date.now() >= token.accessTokenExpires) {
        try {
          const response = await fetch("https://oauth2.googleapis.com/token", {
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            method: "POST",
            body: new URLSearchParams({
              client_id: process.env.AUTH_GOOGLE_ID,
              client_secret: process.env.AUTH_GOOGLE_SECRET,
              grant_type: "refresh_token",
              refresh_token: token.refreshToken,
            }),
          });

          const tokens = await response.json();

          if (!response.ok) throw tokens;

          return {
            ...token,
            accessToken: tokens.access_token,
            accessTokenExpires: Date.now() + tokens.expires_in * 1000,
            refreshToken: tokens.refresh_token ?? token.refreshToken,
          };
        } catch (error) {
          console.error("Error refreshing access token:", error);
          return { ...token, error: "RefreshAccessTokenError" };
        }
      }

      // Check for role updates on every request
      if (token.userId) {
        const currentUser = await db
          .collection("users")
          .findOne({ email: token.email });
        if (currentUser && currentUser.role !== token.role) {
          token.role = currentUser.role;
        }
      }

      return token;
    },

    async session({ session, token }) {
      if (token) {
        session.user.id = token.userId;
        session.user.role = token.role;
        session.accessToken = token.accessToken;
        session.error = token.error;
      }
      return session;
    },
  },
});

here is the auth.config file:

import Google from "next-auth/providers/google";
// import client from "./api/client";

// const db = client.db(process.env.MONGODB_DB); // Define db
// export async function getUserRole(email) {
//   const user = await db.collection("users").findOne({ email });
//   return user?.role || "user";
// }

export default {
  providers: [
    Google({
      clientId: process.env.AUTH_GOOGLE_ID,
      clientSecret: process.env.AUTH_GOOGLE_SECRET,
      authorization: {
        params: {
          prompt: "consent",
          access_type: "offline",
          response_type: "code",
          scope: "openid email profile",
        },
      },
      profile: async (profile) => {
        // Get role from database
        // const role = await getUserRole(profile.email);

        return {
          id: profile.sub,
          name: profile.name,
          email: profile.email,
          image: profile.picture,
          emailVerified: profile.email_verified ? new Date() : null,
          role: "admin",
        };
      },
    }),
  ],
};

here is the client file:

// This approach is taken from https://github.com/vercel/next.js/tree/canary/examples/with-mongodb

import { MongoClient, ServerApiVersion } from "mongodb";

if (!process.env.MONGO_URI) {
  throw new Error('Invalid/Missing environment variable: "MONGO_URI"');
}

const uri = process.env.MONGO_URI;
const options = {
  serverApi: {
    version: ServerApiVersion.v1,
    strict: true,
    deprecationErrors: true,
  },
};

let client;

if (process.env.NODE_ENV === "development") {
  // In development mode, use a global variable so that the value
  // is preserved across module reloads caused by HMR (Hot Module Replacement).
  if (!global._mongoClient) {
    global._mongoClient = new MongoClient(uri, options);
  }
  client = global._mongoClient;
} else {
  // In production mode, it's best to not use a global variable.
  client = new MongoClient(uri, options);
}

// Export a module-scoped MongoClient. By doing this in a
// separate module, the client can be shared across functions.
export default client;

Upvotes: 0

Views: 24

Answers (0)

Related Questions