nextauthjs / next-auth

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

[auth][error] MissingAdapter: Email login requires an adapter (firebase adapter and Resend) #10632

Open tidianeb5 opened 5 months ago

tidianeb5 commented 5 months ago

Adapter type

@auth/firebase-adapter

Environment

  System:
    OS: macOS 14.4.1
    CPU: (14) arm64 Apple M3 Max
    Memory: 4.13 GB / 36.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 21.7.3 - /opt/homebrew/bin/node
    Yarn: 1.22.22 - /opt/homebrew/bin/yarn
    npm: 10.5.0 - /opt/homebrew/bin/npm
  Browsers:
    Chrome: 124.0.6367.60
    Safari: 17.4.1
  npmPackages:
    @auth/firebase-adapter: ^1.6.0 => 1.6.0 
    next: ^14.1.4 => 14.2.2 
    next-auth: ^5.0.0-beta.15 => 5.0.0-beta.16 
    react: ^18.2.0 => 18.2.0 

Reproduction URL

https://github.com/tidianeb5/missing-adapter-issues-firebase-adapter-and-resend

Describe the issue

Issue Description

I'm encountering an error when trying to use email login with the next-auth package:

[auth][error] MissingAdapter: Email login requires an adapter. Read more at https://errors.authjs.dev#missingadapter

at assertConfig (webpack-internal:///(middleware)/./node_modules/@auth/core/lib/utils/assert.js:138:24)
at Auth (webpack-internal:///(middleware)/./node_modules/@auth/core/index.js:88:95)

Configuration

I'm using the following configuration:

According to the documentation, if you have an adapter that is not compatible with the Edge runtime, you need to separate the configuration into two files.

  1. auth.config.ts:

import type { NextAuthConfig } from "next-auth";

import Resend from "next-auth/providers/Resend"; 

export default {
  providers: [
   Resend
  ],
} satisfies NextAuthConfig;
  1. auth.ts (API Route):

import NextAuth from "next-auth";

import authConfig from "@/auth.config";
import { FirestoreAdapter } from "@auth/firebase-adapter";

import { cert } from "firebase-admin/app";

export const {
  handlers: { GET, POST },
  auth,
  signIn,
  signOut,
} = NextAuth({
  ...authConfig,
  pages: {
    signIn: "/auth/login",
    error: "/auth/error",
  },

  adapter: FirestoreAdapter({
    credential: cert({
      projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!,
      clientEmail: process.env.SERVICE_ACCOUNT_CLIENT_EMAIL!,
      privateKey: process.env.SERVICE_ACCOUNT_PRIVATE_KEY!.replace(
        /\\n/g,
        "\n"
      ),
    }),
  }),
  session: { strategy: "jwt" },

});
  1. [...nextauth].ts (API Route):
export { GET, POST } from "@/auth";
  1. 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 null;
  }

  if (isAuthRoute) {
    if (isLoggedIn) {
      return Response.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl))
    }
    return null;
  }

  if (!isLoggedIn && !isPublicRoute) {
    let callbackUrl = nextUrl.pathname;
    if (nextUrl.search) {
      callbackUrl += nextUrl.search;
    }

    const encodedCallbackUrl = encodeURIComponent(callbackUrl);

    return Response.redirect(new URL(
      `/auth/login?callbackUrl=${encodedCallbackUrl}`,
      nextUrl
    ));
  }

  return null;
})

// Optionally, don't invoke Middleware on some paths
export const config = {
  matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
}

Expected Behavior

I expect the email login functionality to work correctly without any errors.

Please let me know if you need any additional information or if I should provide more details about my setup or the steps I've taken to reproduce this issue.

How to reproduce

Just clone the project , and add envs : AUTH_SECRET,SERVICE_ACCOUNT_PRIVATE_KEY,SERVICE_ACCOUNT_CLIENT_EMAIL,NEXT_PUBLIC_FIREBASE_PROJECT_ID ...

Expected behavior

Not showing the error .

NickFoden commented 5 months ago

Hi Mabiri,

I am not a maintainer but saw your issue here and want to give a heads up that next-auth expects firebase-admin of version 11 currently.

if you can try with firebase-admin 11 that might be a path forward. I am trying to raise a PR for updating to firebase-admin 12 at the moment so maybe firebase-admin 12 can work in near future.

Also if you can share any more errors you are getting, does checking types maybe tsc --noEmit flare up any issues from your project ?

tidianeb5 commented 5 months ago

hello NickFoden , thanks for your respond ,

i try firebase-admin version 11.4.1 , but having the same issues ..

ndom91 commented 5 months ago

Thanks for pointing this out, it looks liek this is a bug in the assertion process (checking to make sure yuo've added a database adapter) when using an email adapter and also doing the edge-runtime split config.

Basically, it's checking in the edge runtime (where you don't have the adapter in the auth.js config), to ensure that the provider (resend) can operate correctly. But actually, we're not sending any emails in the middleware (edge runtime) anyway, so we can delay that checking to only happen in places where we would actually be sending an email (like in the normal serverles functions / backend / API route).

I'll try to find some time and see where we can make this change. If anyone wants to take a stab that'd be great too :)

EDIT: Notes for later:

ndom91 commented 5 months ago

For now, since the Email provider actually isn't doing any work in the middleware / edge runtimes where its throwing this error, you can move your Email provider to the config where you have your adapter as well, so they both don't get included in the middleware / edge runtime enviornments.

That should avoid this error and still have your Resend / whatever Email provider be able to send emails and use up verificationTokens upon signin :+1:

tidianeb5 commented 5 months ago

thanks you @ndom91 for your respond , i try mooving the Email provider the config where i have my adapter , but still i am having the same errors...

matthijsgroen commented 5 months ago

I'm having exactly the same issue :-) Curious about a fix!

matthijsgroen commented 5 months ago

I tried using a fake adapter in de middleware part. It won't show errors now, but resend does not work either...

The middleware.ts file

import NextAuth from "next-auth";
import { Adapter, VerificationToken } from "next-auth/adapters";
import { authConfig } from "./auth.config";

const fakeEmailAdapter: Adapter = {
  createVerificationToken: (verificationToken: VerificationToken) => undefined,
  useVerificationToken: (params: { identifier: string; token: string }) => null,
  getUserByEmail: (email: string) => null,
};

export default NextAuth({ ...authConfig, adapter: fakeEmailAdapter }).auth;
tidianeb5 commented 5 months ago

I tried using a fake adapter in de middleware part. It won't show errors now, but resend does not work either...

The middleware.ts file

import NextAuth from "next-auth";
import { Adapter, VerificationToken } from "next-auth/adapters";
import { authConfig } from "./auth.config";

const fakeEmailAdapter: Adapter = {
  createVerificationToken: (verificationToken: VerificationToken) => undefined,
  useVerificationToken: (params: { identifier: string; token: string }) => null,
  getUserByEmail: (email: string) => null,
};

export default NextAuth({ ...authConfig, adapter: fakeEmailAdapter }).auth;

did you find a solution ?

tidianeb5 commented 4 months ago

Hi good morning, anyone have any updates here ? Thank you 🙇 😊

NickFoden commented 4 months ago

nothing ???

Maybe rephrase as


Hi good morning, anyone have any updates here ? Thank you 🙇 
akoskm commented 4 months ago

I'm experiencing the same issue with MongoDB, Prisma, and SendGrid. I also opened a discussion (https://github.com/nextauthjs/next-auth/discussions/10942), but it looks like we're experiencing the same issue.

tidianeb5 commented 4 months ago

nothing ???

Maybe rephrase as

Hi good morning, anyone have any updates here ? Thank you 🙇 

thank you , i will take it in mind , next time , 👌

akoskm commented 4 months ago

For now, since the Email provider actually isn't doing any work in the middleware / edge runtimes where its throwing this error, you can move your Email provider to the config where you have your adapter as well, so they both don't get included in the middleware / edge runtime enviornments.

That should avoid this error and still have your Resend / whatever Email provider be able to send emails and use up verificationTokens upon signin 👍

This is a good idea! I don't want to do anything in my middleware because I use const session = await auth() on my pages. However, when I include the default middleware config suggested here https://authjs.dev/getting-started/installation?framework=next.js

while having this in auth.js:

import NextAuth from "next-auth";
import Sendgrid from "next-auth/providers/sendgrid";
import type { DefaultSession } from "next-auth";

declare module "next-auth" {
  interface Session {
    user: {
      credits?: number;
    } & DefaultSession["user"];
  }
}

import { PrismaAdapter } from "@auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Sendgrid({
      apiKey: process.env.AUTH_SENDGRID_KEY,
      from: process.env.AUTH_SENDGRID_FROM,
    }),
  ],
  adapter: PrismaAdapter(prisma),
});

the app works between subsequent refreshes, and I'm getting no errors in the console, but once I log in and I have a session in cookies, I'm getting the following error:

Screenshot 2024-05-21 at 21 11 28

Do you have any suggestions on how to fix this if i don't want to do anything in my middleware?

Any help is appreciated! 🙏

NickFoden commented 4 months ago

Hi @akoskm I think you want to initialize the prisma client differently because of hot reloading etc.

Check prisma + next.js examples for the recommended approach

I use this / forget where I picked it up from, but similarly I use this below for next.js projects (and I also do not use the app router, am waiting for more stable/mature release that is less verbose and also when "we" have React 19 etc)


// src/adapters/prisma.ts

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

const prismaClientSingleton = () => {
  return new PrismaClient();
};

type PrismaClientSingleton = ReturnType<typeof prismaClientSingleton>;

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClientSingleton | undefined;
};

const prisma = globalForPrisma.prisma ?? prismaClientSingleton();

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

export default prisma;
akoskm commented 4 months ago

Thanks @NickFoden! I know where you got that from 😃 I asked the AI on the Prisma site what's the best way to expose the Prisma client object, and it replied with a similar code:

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

const prismaClientSingleton = () => {
  return new PrismaClient();
};

declare const globalThis: {
  prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;

const prisma = globalThis.prismaGlobal ?? prismaClientSingleton();

export default prisma;

if (process.env.NODE_ENV !== "production") globalThis.prismaGlobal = prisma;

I tried the code you suggested as well - which does almost the same thing - but I'm getting the same error.

akoskm commented 4 months ago

For now, since the Email provider actually isn't doing any work in the middleware / edge runtimes where its throwing this error, you can move your Email provider to the config where you have your adapter as well, so they both don't get included in the middleware / edge runtime enviornments.

That should avoid this error and still have your Resend / whatever Email provider be able to send emails and use up verificationTokens upon signin 👍

hey @ndom91 I'd love to give this a shot and create PR that fixes the issue based on your first comment. However, after reading this, I'm curious if you're aware that the workaround that was suggested doesn't make the issue go away.

Considering that, do you think simply moving the check is sufficient? I can give it a shot after work and see if it fixes the issue and if the login/session still works.

ndom91 commented 4 months ago

@akoskm Yeah, go for it!

I think it shuold be sufficient, but haven't had time to dig in much further myself. We'd appreciate if you or anyone else could spend some time digging into this a bit more :pray: . It might be as simple as just moving that assertion to a bit later in the process.

ndom91 commented 4 months ago

Also regarding the prisma errors, it seems like Prisma is still tryign to be executed on the edge runtime somewhere. What version of prisma are you using? As of 5.12.0-ish it should be more "edge compatible" in certain situations.

ppulwey commented 4 months ago

Hey @ndom91 any news here? I'm still facing this error. Is there anything we can support with?

ndom91 commented 4 months ago

@ppulwey no updates that I'm aware of.

@akoskm do you still plan on working on this? :pray:

akoskm commented 3 months ago

@ppulwey no updates that I'm aware of.

@akoskm do you still plan on working on this? 🙏

Sorry, I had to ship something in a limited time and just went with a different tech.

ppulwey commented 3 months ago

@ppulwey no updates that I'm aware of. @akoskm do you still plan on working on this? 🙏

Sorry, I had to ship something in a limited time and just went with a different tech.

No problem. We are in the process of setting up a new project and would like to start directly with version 5. Magic Links are a prerequisite for the project. Is there an approximate estimate of how long it will take to publish the fix?

sanneh2 commented 3 months ago

This is a common problem when using the standard middleware.ts middleware from next.js (any version) in combination with next-auth various providers and credentials. I've noticed that's it's also a bit of a gamble, since in some simple case (when you don't really need it to work) it will work and in some cases it's not. I wish it would work more reliably.

For those in a time sensitive situation. The workaround that worked for me in many projects(shoutout to next.auth) is that I would use the built in session checkers and not the middleware option . In the v5 version it is: (works in v4 version with getSession method or similar)

const session = await auth();

  if (!session) {
    redirect("/login");
  }

The server sided code on your page to verify the session and act accordingly, redirect or other.

The caveat is that you have to manually go to each page (and API route) and protect them manually. Be careful to not forget any admin pages or important API routes, but this manual session verification is a solid way to use the next-auth without the middleware!!

ppulwey commented 3 months ago

This is a common problem when using the standard middleware.ts middleware from next.js (any version) in combination with next-auth various providers and credentials. I've noticed that's it's also a bit of a gamble, since in some simple case (when you don't really need it to work) it will work and in some cases it's not. I wish it would work more reliably.

For those in a time sensitive situation. The workaround that worked for me in many projects(shoutout to next.auth) is that I would use the built in session checkers and not the middleware option . In the v5 version it is: (works in v4 version with getSession method or similar)

const session = await auth();

  if (!session) {
    redirect("/login");
  }

The server sided code on your page to verify the session and act accordingly, redirect or other.

The caveat is that you have to manually go to each page (and API route) and protect them manually. Be careful to not forget any admin pages or important API routes, but this manual session verification is a solid way to use the next-auth without the middleware!!

Hey @sanneh2. You can also move the check to a layout and use the folder structure to solve this. But I think this is not the error we're currently facing.

taylor-lindores-reeves commented 2 months ago

Have tried finding a solution to this on numerous occasions. It seems as though AuthJS simply does not work whatsoever with a simple Magic Link set-up combined with database sessions and next-auth wrapped middleware. In any case, it always errors:

MissingAdapter: Email login requires an adapter.

The AuthJS edge compatibility documentation does not make it clear that password-less login is incompatible with Prisma adapter configuration, even with Split Config outlined in the docs. Have not personally tried OAuth providers, but I guess they should make it clear in the docs that you're gonna waste your time trying to get Email-Only working with middleware/Prisma adapter.

For now, the solution for me is a generic function that uses next/navigation redirect on a per-page basis.

ndom91 commented 2 months ago

Yeah so the issue is that many of the adapter's don't work in middleware (edge runtimes), as they rely on Node.js APIs that aren't available in the JS runtimes hosting providers use for their edge functions, like in Vercel's Middleware environment.

Other adpaters, however, have begun to rectify this and refactor their JS clients. For example, Prisma as of 5.12.0 does support middleware / edge runtimes with certain upstream database adapters, check out our docs page on it.

However, if you want to skip all the potential complexities that come with running your database in middleware, checking for the session in a server component like @sanneh2 showed in their comment is a totally valid way to do it too :+1:

Joshuajrodrigues commented 2 months ago

So I had a similar issue with drizzle, this is how I solved it

in auth.config.ts

import type { NextAuthConfig } from "next-auth"
import GitHub from "next-auth/providers/github"
import Google from "next-auth/providers/google"

// Notice this is only an object, not a full Auth.js instance

export default {
    providers: [GitHub, Google],
} satisfies NextAuthConfig

in my auth.ts

import { DrizzleAdapter } from "@auth/drizzle-adapter"
import NextAuth from "next-auth"
import authConfig from "./auth.config"
import { db } from "./db"
import Resend from "next-auth/providers/resend"

const combinedProviders = [
    ...authConfig.providers,
    Resend({
        from: 'email@domain.com',
    }),
];
export const { handlers, signIn, signOut, auth } = NextAuth({
    providers: combinedProviders,
    adapter: DrizzleAdapter(db),
    session: { strategy: "jwt" },
})

Works great so far

Fmd0 commented 2 months ago

So I had a similar issue with drizzle, this is how I solved it

in auth.config.ts

import type { NextAuthConfig } from "next-auth"
import GitHub from "next-auth/providers/github"
import Google from "next-auth/providers/google"

// Notice this is only an object, not a full Auth.js instance

export default {
    providers: [GitHub, Google],
} satisfies NextAuthConfig

in my auth.ts

import { DrizzleAdapter } from "@auth/drizzle-adapter"
import NextAuth from "next-auth"
import authConfig from "./auth.config"
import { db } from "./db"
import Resend from "next-auth/providers/resend"

const combinedProviders = [
    ...authConfig.providers,
    Resend({
        from: 'email@domain.com',
    }),
];
export const { handlers, signIn, signOut, auth } = NextAuth({
    providers: combinedProviders,
    adapter: DrizzleAdapter(db),
    session: { strategy: "jwt" },
})

Works great so far

Pretty good, I think one very important point is that we should put email provider(Resend) and database adapter in the same NextAuthConfig object, or it will cause error. And official example has some misleading tendency, they deliberately split the provider and the other NextAuthConfig object. For email provider, we shouldn't split it.