next-auth
next-auth copied to clipboard
Auth.js example Nextjs site breaks when integrating @auth/mongodb-adapter from example code
Adapter type
@auth/mongodb-adapter
Environment
System: OS: Windows 10 10.0.19045 CPU: (16) x64 AMD Ryzen 7 4800H with Radeon Graphics Memory: 7.99 GB / 31.42 GB Browsers: Edge: Chromium (121.0.2277.112) Internet Explorer: 11.0.19041.3636 npmPackages: @auth/mongodb-adapter: ^2.4.0 => 2.4.0 next: latest => 14.1.0 next-auth: beta => 5.0.0-beta.11 react: ^18.2.0 => 18.2.0
Reproduction URL
https://github.com/FrankTrog/next-auth/tree/main/apps/examples/nextjs
Describe the issue
When following the instructions from Auth.js to install the @auth/mongodb-adapter in order to use MongoDB for session storage many errors are thrown by the middleware in the terminal regarding mongodb. It appears there are modules required by mongodb that cannot be resolved. Also, there is an error about the edge runtime not supporting Node.js 'stream' module. It is surprising to me that there are so many errors like this from following official documentation and using the example respository directly from Auth.js.
Mongodb adapter documentation referenced
Missing modules
- kerberos
- @mongodb-js/zstd
- @aws-sdk/credential-providers
- gcp-metadata
- snappy
- socks
- aws4
- mongodb-client-encryption
Terminal output ` npm run dev
dev next
▲ Next.js 14.1.0
- Local: http://localhost:3000
- Environments: .env.local
✓ Ready in 1912ms ○ Compiling /middleware ... ⚠ ./node_modules/mongodb/lib/deps.js Module not found: Can't resolve 'kerberos' in 'next-auth\apps\examples\nextjs\node_modules\mongodb\lib'
Import trace for requested module: ./node_modules/mongodb/lib/deps.js ./node_modules/mongodb/lib/client-side-encryption/client_encryption.js ./node_modules/mongodb/lib/index.js ./components/mdbconn.tsx ./auth.ts
./node_modules/mongodb/lib/deps.js Module not found: Can't resolve '@mongodb-js/zstd' in 'next-auth\apps\examples\nextjs\node_modules\mongodb\lib'
Import trace for requested module: ./node_modules/mongodb/lib/deps.js ./node_modules/mongodb/lib/client-side-encryption/client_encryption.js ./node_modules/mongodb/lib/index.js ./components/mdbconn.tsx ./auth.ts
./node_modules/mongodb/lib/deps.js Module not found: Can't resolve '@aws-sdk/credential-providers' in 'next-auth\apps\examples\nextjs\node_modules\mongodb\lib'
Import trace for requested module: ./node_modules/mongodb/lib/deps.js ./node_modules/mongodb/lib/client-side-encryption/client_encryption.js ./node_modules/mongodb/lib/index.js ./components/mdbconn.tsx ./auth.ts
./node_modules/mongodb/lib/deps.js Module not found: Can't resolve 'gcp-metadata' in 'next-auth\apps\examples\nextjs\node_modules\mongodb\lib'
Import trace for requested module: ./node_modules/mongodb/lib/deps.js ./node_modules/mongodb/lib/client-side-encryption/client_encryption.js ./node_modules/mongodb/lib/index.js ./components/mdbconn.tsx ./auth.ts
./node_modules/mongodb/lib/deps.js Module not found: Can't resolve 'snappy' in 'next-auth\apps\examples\nextjs\node_modules\mongodb\lib'
Import trace for requested module: ./node_modules/mongodb/lib/deps.js ./node_modules/mongodb/lib/client-side-encryption/client_encryption.js ./node_modules/mongodb/lib/index.js ./components/mdbconn.tsx ./auth.ts
./node_modules/mongodb/lib/deps.js Module not found: Can't resolve 'socks' in 'next-auth\apps\examples\nextjs\node_modules\mongodb\lib'
Import trace for requested module: ./node_modules/mongodb/lib/deps.js ./node_modules/mongodb/lib/client-side-encryption/client_encryption.js ./node_modules/mongodb/lib/index.js ./components/mdbconn.tsx ./auth.ts
./node_modules/mongodb/lib/deps.js Module not found: Can't resolve 'aws4' in 'next-auth\apps\examples\nextjs\node_modules\mongodb\lib'
Import trace for requested module: ./node_modules/mongodb/lib/deps.js ./node_modules/mongodb/lib/client-side-encryption/client_encryption.js ./node_modules/mongodb/lib/index.js ./components/mdbconn.tsx ./auth.ts
./node_modules/mongodb/lib/deps.js Module not found: Can't resolve 'mongodb-client-encryption' in 'next-auth\apps\examples\nextjs\node_modules\mongodb\lib'
Import trace for requested module:
./node_modules/mongodb/lib/deps.js
./node_modules/mongodb/lib/client-side-encryption/client_encryption.js
./node_modules/mongodb/lib/index.js
./components/mdbconn.tsx
./auth.ts
⨯ Error: The edge runtime does not support Node.js 'stream' module.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
at
localhost:3000
I am most of the way through converting a project and stuck at this point. Guidance would be fantastic. Thanks!
How to reproduce
- Fork & Clone/Download https://github.com/FrankTrog/next-auth/tree/main/apps/examples/nextjs
- CD into next-auth/apps/examples/nextjs
- run
npm i - run
npm run dev - Open
localhost:3000in browser of choice - Review terminal for errors
Expected behavior
Example site built with Auth.js should be usable without errors triggered by MongoDB.
This may be similar to #42277 and #42313 which were supposedly resolved in v13.0.2-canary.o of Next.js. That issue was closed though and this issue is regarding the latest version of Authjs and Nextjs.
I am having the same issue with [email protected], [email protected] and @auth/[email protected]. Adding mongodb to serverComponentsExternalPackages didn't solve the problem.
I'm having the same issue. Google and GitHub providers works, but credentials don't.
Error happens when i set the following code line in auth.config.ts, at the credentials provider authorize() function
const user = await getUserByEmail(email);
Environment
Local mongodb
Windows 11 Home compilation 22631.3155
node v20.9.0
Error msgs
msg on Google Chrome v 122.0.6261.71: Server Error Error: The edge runtime does not support Node.js 'stream' module. Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
msg on terminal:
Error: The edge runtime does not support Node.js 'stream' module.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime
at
auth.config.ts
Error happens in authorize(), when consulting db for a user email
import type { NextAuthConfig } from "next-auth";
import Credentials from "next-auth/providers/credentials";
import Github from "next-auth/providers/github";
import Google from "next-auth/providers/google";
import bcrypt from "bcryptjs";
import { LoginSchema } from "@/schemas";
import { getUserByEmail } from "@/data/user";
export default {
providers: [
Google,
Github,
Credentials({
async authorize(credentials) {
const validatedFields = LoginSchema.safeParse(credentials);
if (validatedFields.success) {
const { email, password } = validatedFields.data;
const user = await getUserByEmail(email);
if (!user || !user.password) return null;
const passwordsMatch = await bcrypt.compare(
password,
user.password,
);
if (passwordsMatch) {
return user;
}
}
return null;
}
})
],
} satisfies NextAuthConfig;
Here is the getUserByEmail():
import clientPromise from "@/lib/db";
export const getUserByEmail = async (email: string) => {
try {
// Connect to the database using the clientPromise
const client = await clientPromise;
const db = client.db("myDatabaseName");
// Try to find an existing user by email
const user = await db.collection("users").findOne({ email });
return user;
} catch {
return null;
}
};
auth.js
strategy is set to jwt, but it seems it keep trying to use database on edge.
import NextAuth from "next-auth"
import authConfig from "@/auth.config";
import { MongoDBAdapter } from "@auth/mongodb-adapter"
import clientPromise from "@/lib/db"
export const {
handlers: { GET, POST },
auth,
signIn,
signOut, } = NextAuth({
adapter: MongoDBAdapter(clientPromise),
session: {strategy: "jwt"},
...authConfig,
pages: {
signIn: '/auth/login',
signOut: '/auth/login',
error: '/auth/error',
},
callbacks: {
},
})
middleware.ts
import NextAuth from "next-auth";
import authConfig from "@/auth.config";
import {
DEFAULT_LOGIN_REDIRECT,
apiAuthPrefix,
authRoutes,
publicRoutes,
} from "@/routes";
const { auth } = NextAuth(authConfig);
export default auth((req) => {
const { nextUrl } = req;
const isLoggedIn = !!req.auth;
const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
const isAuthRoute = authRoutes.includes(nextUrl.pathname);
if (isApiAuthRoute) {
return;
}
if (isAuthRoute) {
if (isLoggedIn) {
return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl))
}
return;
}
if (!isLoggedIn && !isPublicRoute) {
return Response.redirect(new URL("auth/login", nextUrl))
}
return;
})
// Optionally, don't invoke Middleware on some paths
export const config = {
matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
}
db.ts
// This approach is taken from https://github.com/vercel/next.js/tree/canary/examples/with-mongodb
import { MongoClient } from "mongodb"
declare global {
var _mongoClientPromise: Promise<MongoClient>;
}
if (!process.env.MONGODB_URI) {
throw new Error('Invalid/Missing environment variable: "MONGODB_URI"')
}
const uri = process.env.MONGODB_URI
const options = {}
let client
let clientPromise: Promise<MongoClient>
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._mongoClientPromise) {
client = new MongoClient(uri, options)
global._mongoClientPromise = client.connect()
}
clientPromise = global._mongoClientPromise
} else {
// In production mode, it's best to not use a global variable.
client = new MongoClient(uri, options)
clientPromise = client.connect()
}
// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise
dependencies
{
"name": "next-project",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@auth/mongodb-adapter": "^2.4.0",
"bcryptjs": "^2.4.3",
"mongodb": "^6.3.0",
"next": "14.1.0",
"next-auth": "^5.0.0-beta.13",
"next-themes": "^0.2.1",
"nodemailer": "^6.9.10",
"react": "^18",
"react-day-picker": "^8.10.0",
"react-dom": "^18",
"react-hook-form": "^7.50.1",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.1.0",
"postcss": "^8",
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}
Hey @rgno1, did you find a solution?
@ann0nip were you able to find the solution?
@rgno1 did you find the solution. I am also facing the same problem.
this is an issue with MongoDB not being fully edge compatible, sorry that the doc is not super clear about this. Here's my suggestion - this works for Prisma before they added the Edge runtime support recently:
Define the auth.config.ts file:
export default {
providers: [
GitHub,
Google,
Facebook,
Twitter,
//.. other providers
],
} satisfies NextAuthConfig
Import and use the MongoDBAdapter in the auth.ts file:
import NextAuth from "next-auth"
import authConfig from "auth.config"
import { MongoDBAdapter } from "@auth/mongodb-adapter"
export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({
adapter: PrismaAdapter(globalThis.prisma),
session: { strategy: "jwt" },
...authConfig,
})
edit: Note that it's important to use the JWT strategy here to avoid the DB call in the middleware.
In the middleware file, import the auth.config and use it as the configuration for NextAuth (note that the adapter is not used here to avoid the error):
import NextAuth from "next-auth"
import authConfig from "auth.config"
export const middleware = NextAuth(authConfig).auth
Let me know if this helps. Close as the error is not from next-auth or @auth/mongodb-adapter
@ThangHuuVu Hey, would be really helpful if you can guide me how to use this strategy in Client side components to signIn for example. I am getting requestAsyncStorage not available.
@ThangHuuVu Hey, would be really helpful if you can guide me how to use this strategy in Client side components to signIn for example. I am getting requestAsyncStorage not available.
Please open another issue with a minimal reproduction, 🙏 thank you
this is an issue with MongoDB not being fully edge compatible, sorry that the doc is not super clear about this. Here's my suggestion - this works for
Prismabefore they added the Edge runtime support recently:Define the
auth.config.tsfile:export default { providers: [ GitHub, Google, Facebook, Twitter, //.. other providers ], } satisfies NextAuthConfigImport and use the
MongoDBAdapterin theauth.tsfile:import NextAuth from "next-auth" import authConfig from "auth.config" import { MongoDBAdapter } from "@auth/mongodb-adapter" export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({ adapter: PrismaAdapter(globalThis.prisma), session: { strategy: "jwt" }, ...authConfig, })In the middleware file, import the
auth.configand use it as the configuration forNextAuth(note that theadapteris not used here to avoid the error):import NextAuth from "next-auth" import authConfig from "auth.config" export const middleware = NextAuth(authConfig).authLet me know if this helps. Close as the error is not from
next-author@auth/mongodb-adapter
Can you elaborate a bit more on the middleware part? I followed your instructions (as well as the page on this in the docs), but I'm unsure how to then reference the database user without having access to the database adapter in the middleware.
import { NextResponse } from "next/server";
import authConfig from "./auth.config";
import NextAuth from "next-auth";
const { auth } = NextAuth(authConfig);
export default auth((req) => {
const url = req.nextUrl.clone();
url.pathname = "/";
if (req.auth?.user?.role == "admin") {
console.log("User: " + JSON.stringify(req.auth.user));
}
});
export const config = {
matcher: ["/api/:path*"],
};
This results in all calls in the app throwing this error:
[auth][error] MissingAdapter: Database session requires an adapter.. Read more at https://errors.authjs.dev#missingadapter at assertConfig (webpack-internal:///(middleware)/./node_modules/@auth/core/lib/utils/assert.js:146:24) at Auth (webpack-internal:///(middleware)/./node_modules/@auth/core/index.js:88:95)
This is for MongoDB Adapter
@IlyyA the keys are:
- Use the JWT strategy
- Don’t import the adapter into the middleware
This can work because we only use the middleware for checking the session in the auth method. Let me know if this helps!
Hey @rgno1, did you find a solution?
I opted not to use Prisma in my project, so I resolved the issue by implementing an API route, which worked. Using prisma also work.
auth.config.ts
export default {
providers: [
Google,
//Github,
Credentials({
async authorize(credentials) {
const validatedFields = LoginSchema.safeParse(credentials);
if (validatedFields.success) {
const { email, password } = validatedFields.data;
const response = await fetch(`http://localhost:${process.env.PORT}/api/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
password
})
})
const user = await response.json()
if (response.ok && user) {
return user
}
return null;
}
return null;
}
})
],
} satisfies NextAuthConfig;
/api/users end point
import bcrypt from 'bcryptjs';
import { getUserByEmail } from '@/data/user';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
try {
// Parsing JSON body in Edge Middleware
const body = await req.json();
const { email, password } = body;
// Basic validation
if (!email || !password) {
return new NextResponse(JSON.stringify({ message: 'Email and password are required' }), {
status: 400,
headers: {
"Content-Type": "application/json",
},
});
}
// Simulated getUserByEmail and bcrypt.compare (must be compatible with Edge runtime)
const user = await getUserByEmail(email); // This function needs to be compatible with Edge runtime
if (!user || !user.password) {
// User not found
return new NextResponse(JSON.stringify({ message: 'Invalid credentials' }), {
status: 401,
headers: {
"Content-Type": "application/json",
},
});
}
// Check if the password is correct
const isMatch = await bcrypt.compare(password, user.password); // Simulated, needs to be Edge-compatible
if (!isMatch) {
// Password does not match
return new NextResponse(JSON.stringify({ message: 'Invalid credentials' }), {
status: 401,
headers: {
"Content-Type": "application/json",
},
});
}
// Successfully authenticated
const { password: _, ...userWithoutPassword } = user; // Remove the password from the user object before sending
return new NextResponse(JSON.stringify(userWithoutPassword), {
status: 200,
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
console.error(error);
return new NextResponse(JSON.stringify({ message: 'Internal server error' }), {
status: 500,
headers: {
"Content-Type": "application/json",
},
});
}
}
@IlyyA the keys are:
- Use the JWT strategy
- Don’t import the adapter into the middleware
This can work because we only use the middleware for checking the session in the
authmethod. Let me know if this helps!
So using the database strategy is not possible along with any middleware auth, as it is not possible to get the Adapter/req.auth.user object in the middleware without throwing the error?
In my case, I am using email provider, so I had to do this in the middleware to avoid issues with the auth config validation:
import NextAuth from "next-auth"
import authConfig from "./auth.config"
export const middleware = NextAuth({
...authConfig,
adapter: {
createVerificationToken: () => { console.log('middleware createVerificationToken')},
useVerificationToken: () => { console.log('middleware useVerificationToken')},
getUserByEmail: () => { console.log('middleware getUserByEmail')}
} as unknown
}).auth
Otherwise it would say:
[auth][error] MissingAdapter: Email login requires an adapter.. Read more at https://errors.authjs.dev#missingadapter