nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.17k stars 3.35k forks source link

Error: Prisma Client is unable to run in an edge runtime. #9872

Closed SebastianNarvaez11 closed 7 months ago

SebastianNarvaez11 commented 7 months ago

image

I am setting up a project with: NextJS v14 MUI v5 AuthJS v5 Prisma PostgreSQL The application is hosted on Vercel, including the PostgreSQL storage

I am following this guide to implement authentication with AuthJS: https://youtu.be/1MTyCvS05V4?si=g5vTsEXzF8n07z4o

I am around 2,5 hours into the guide, where the database connection is set up. Until my last commit everything worked fine. I could register a new user and had no errors. No "edge runtime" issues at all.

Since my recent changes, I receive the posted error locally. I debugged this error to the following line in my @/auth.config.ts file (see comment)

import bcrypt from "bcryptjs";
import type { NextAuthConfig } from "next-auth";
import Credentials from "next-auth/providers/credentials";

import { LoginSchema } from "@/data/schemas/auth";
import { getUserByEmail } from "@/data/user";

export default {
    providers: [
        Credentials({
            async authorize(credentials) {
                const validatedFields = LoginSchema.safeParse(credentials);

                if (!validatedFields.success) {
                    // Invalid credentials by schema validations
                    return null;
                }

                const { email, password } = validatedFields.data;
                // todo: Prisma Client is unable to run in an edge runtime.
                // This line throws the above error!
                const user = await getUserByEmail(email);

                if (!user) {
                    // User doesn't exist, invalid login credentials
                    return null;
                }

                if (!user.password) {
                    // User hasn't set a password, they registered with an OAuth Provider (google, github, ...)
                    return null;
                }

                const passwordsMatch = await bcrypt.compare(
                    password,
                    user.password
                );

                if (!passwordsMatch) {
                    // Password incorrect
                    return null;
                }

                return user;
            }
        })
    ],
} satisfies NextAuthConfig;

Here are other relevant files:

@/middleware.ts

import NextAuth from "next-auth";
import { NextResponse } from "next/server";

import authConfig from "@/auth.config";
import {
    API_AUTH_PREFIX,
    ROUTES,
    REDIRECTS,
} from "@/routes.config";

const { auth } = NextAuth(authConfig);

export default auth((req) => {
    const { nextUrl } = req;
    const isLoggedIn = !!req.auth;

    // Route protection rules
    const isApiAuthRoute = nextUrl.pathname.startsWith(API_AUTH_PREFIX);
    const isAuthRoute = ROUTES.auth.includes(nextUrl.pathname);
    const isPublicRoute = ROUTES.public.includes(nextUrl.pathname);
    // todo Implement admin route logic
    // const isAdminRoute = ROUTES.admin.includes(nextUrl.pathname);

    if (isApiAuthRoute) {
        return null;
    }

    if (isAuthRoute) {
        if (isLoggedIn) {
            return NextResponse.redirect(
                new URL(REDIRECTS.afterLogin, nextUrl)
            );
        }
        return null;
    }

    if (!isLoggedIn && !isPublicRoute) {
        return NextResponse.redirect(
            new URL(REDIRECTS.privateRoute, nextUrl)
        );
    }

    return null;
});

// Applies this middleware only to files in the app directory
export const config = {
    matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};

@/auth.ts

import NextAuth from "next-auth";
import { PrismaAdapter } from "@auth/prisma-adapter";

import { db } from "@/lib/db";
import authConfig from "@/auth.config";

export const {
    handlers: { GET, POST },
    auth,
    signIn,
    signOut,
} = NextAuth({
    adapter: PrismaAdapter(db),
    session: { strategy: "jwt" },
    ...authConfig,
});

@/db.ts

import { PrismaClient } from "@prisma/client";

declare global {
    var prisma: PrismaClient | undefined;
}

export const db = globalThis.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") {
    globalThis.prisma = db;
}

@/data/user.ts

import { db } from "@/lib/db";
import { User } from "@prisma/client";

export async function getUserById(id: string): Promise<User | null> {
    try {
        return await db.user.findUnique({
            where: {
                id
            }
        });
    } catch {
        return null;
    }
}

export async function getUserByEmail(email: string): Promise<User | null> {
    try {
        return await db.user.findUnique({
            where: {
                email
            }
        });
    } catch {
        return null;
    }
}

@/app/api/auth/[...nextauth]/route.ts

export { GET, POST } from "@/auth";

Anything I can find on Github, other google research or also chatgpt consultation can't help me fix this!

Originally posted by @schilffarth in https://github.com/nextauthjs/next-auth/discussions/9860

balazsorban44 commented 7 months ago

This is an upstream issue with Prisma, you can take it up with them. Workaround:

https://authjs.dev/guides/upgrade-to-v5#edge-compatibility

When opening an issue, please follow the bug template.