reactjsauthenticationnext.jsnext-authnextjs14

Keep getting CallbackRouteError fired when trying to use signIn function in (next-)authjs v5


I am a beginner/intermediate developer attempting to create a NextJS 14 powered app for a friend of mine, but I seem to keep running into the same problem.

I am loosely following this tutorial by CodeWithAntonio with some slight tweaks to work for me. I have already tried going back through the tutorial more times than I can count to find a fix, but seemingly I cannot find one.

I am using NextJS 14 App-directory, with a Neon database and the Prisma adapter due to some incompatibilities with DrizzleORM in its current state.

This is my auth.ts file in the root directory:

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

import { db } from "@/lib/db";
import authConfig from "@/auth.config";
import { getUserById } from "@/lib/user";
import { UserRole } from "@prisma/client";

export const {
  handlers: { GET, POST },
  auth,
  signIn,
  signOut,
} = NextAuth({
  pages: {
    signIn: "/auth/login",
    error: "/auth/error",
  },
  callbacks: {
    async signIn({ user, account }) {
      if (account?.provider !== "credentials") return true;

      return true;
    },
    async session({ token, session }) {
      if (token.sub && session.user) {
        session.user.id = token.sub;
      }

      if (token.role && session.user) {
        session.user.role = token.role as UserRole;
      }

      return session;
    },
    async jwt({ token }) {
      if (!token.sub) return token;

      // this grabs the data from the db on the user from the user id on the token
      const existingUser = await getUserById(token.sub);

      if (!existingUser) return token;

      token.role = existingUser.role;

      return token;
    },
  },
  adapter: PrismaAdapter(db),
  session: { strategy: "jwt", maxAge: 60 * 60 * 24 },
  ...authConfig,
});

and this is my auth.config.ts file also in the root:

import bcrypt from "bcryptjs";

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

import { LoginSchema } from "@/schemas";
import { getUserByEmail } from "./lib/user";

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

        if (validatedFields.success) {
          const { email, password } = validatedFields.data;

          // gets the user data from the db by email
          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;
        }

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

I believe these are all the files useful to the error which looks like this for context:

Read more at https://errors.authjs.dev/#credentialssignin
[auth][error] CallbackRouteError: Read more at https://errors.authjs.dev#callbackrouteerror
[auth][cause]: Error
    at Module.callback (webpack-internal:///(action-browser)/./node_modules/@auth/core/lib/actions/callback/index.js:226:23)
    at async AuthInternal (webpack-internal:///(action-browser)/./node_modules/@auth/core/lib/index.js:66:24)
    at async Auth (webpack-internal:///(action-browser)/./node_modules/@auth/core/index.js:126:34)
    at async signIn (webpack-internal:///(action-browser)/./node_modules/next-auth/lib/actions.js:51:17)
    at async $$ACTION_0 (webpack-internal:///(action-browser)/./actions/login.ts:46:9)
    at async F:\lng-v3\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:39:418
    at async rw (F:\lng-v3\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:38:7978)
    at async r4 (F:\lng-v3\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:41:1251)
    at async doRender (F:\lng-v3\node_modules\next\dist\server\base-server.js:1438:30)
    at async cacheEntry.responseCache.get.routeKind (F:\lng-v3\node_modules\next\dist\server\base-server.js:1599:28)
    at async DevServer.renderToResponseWithComponentsImpl (F:\lng-v3\node_modules\next\dist\server\base-server.js:1507:28)
    at async DevServer.renderPageComponent (F:\lng-v3\node_modules\next\dist\server\base-server.js:1931:24)
    at async DevServer.renderToResponseImpl (F:\lng-v3\node_modules\next\dist\server\base-server.js:1969:32)
    at async DevServer.pipeImpl (F:\lng-v3\node_modules\next\dist\server\base-server.js:920:25)
    at async NextNodeServer.handleCatchallRenderRequest (F:\lng-v3\node_modules\next\dist\server\next-server.js:272:17)
    at async DevServer.handleRequestImpl (F:\lng-v3\node_modules\next\dist\server\base-server.js:816:17)
    at async F:\lng-v3\node_modules\next\dist\server\dev\next-dev-server.js:339:20
    at async Span.traceAsyncFn (F:\lng-v3\node_modules\next\dist\trace\trace.js:154:20)
    at async DevServer.handleRequest (F:\lng-v3\node_modules\next\dist\server\dev\next-dev-server.js:336:24)
    at async invokeRender (F:\lng-v3\node_modules\next\dist\server\lib\router-server.js:174:21)
    at async handleRequest (F:\lng-v3\node_modules\next\dist\server\lib\router-server.js:353:24)
    at async requestHandlerImpl (F:\lng-v3\node_modules\next\dist\server\lib\router-server.js:377:13)
    at async Server.requestListener (F:\lng-v3\node_modules\next\dist\server\lib\start-server.js:141:13)
[auth][details]: {
  "provider": "credentials"
}

This is the server action (login.ts) that actually logs the user in and is called directly from the login form:

"use server";

import * as z from "zod";

import { LoginSchema } from "@/schemas";
import { signIn } from "@/auth";
import { DEFAULT_LOGIN_REDIRECT } from "@/routes";
import { AuthError } from "next-auth";
import { getUserByEmail } from "@/lib/user";
import { generateVerificationToken } from "@/lib/tokens";

export const login = async (values: z.infer<typeof LoginSchema>) => {
  const validatedFields = LoginSchema.safeParse(values);

  if (!validatedFields.success) {
    return { error: "Invalid fields!" };
  }

  const { email, password } = validatedFields.data;

  // gets user data from the db by email
  const existingUser = await getUserByEmail(email);

  if (!existingUser || !existingUser.email || !existingUser.password) {
    return { error: "Your email and/or password are incorrect." };
  }

  if (!existingUser.email_verified) {
    // creates and returns a verification token for the user from the db
    const verificationToken = await generateVerificationToken(
      existingUser.email
    );

    return { success: "Verification email sent!" };
  }

  try {
    await signIn("credentials", {
      email,
      password,
      // currently just set to /settings page
      redirectTo: DEFAULT_LOGIN_REDIRECT,
    });
  } catch (error) {
    // this is what seems to be returning the error message "CallbackRouteError"
    console.log(error);
    if (error instanceof AuthError) {
      switch (error.type) {
        case "CredentialsSignin":
          return { error: "Your email and/or password are incorrect." };
        default:
          return { error: "Something went wrong!" };
      }
    }
    throw error;
  }
};

When using the signIn() function through the server action (login.ts) in my login form passing the email and password in string format, I get an error console logged saying:

Read more at https://errors.authjs.dev/#credentialssignin
[auth][error] CallbackRouteError: Read more at https://errors.authjs.dev#callbackrouteerror
[auth][cause]: Error
    at Module.callback (webpack-internal:///(action-browser)/./node_modules/@auth/core/lib/actions/callback/index.js:226:23)
    at async AuthInternal (webpack-internal:///(action-browser)/./node_modules/@auth/core/lib/index.js:66:24)
    at async Auth (webpack-internal:///(action-browser)/./node_modules/@auth/core/index.js:126:34)
    at async signIn (webpack-internal:///(action-browser)/./node_modules/next-auth/lib/actions.js:51:17)
    at async $$ACTION_0 (webpack-internal:///(action-browser)/./actions/login.ts:46:9)
    at async F:\lng-v3\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:39:418
    at async rw (F:\lng-v3\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:38:7978)
    at async r4 (F:\lng-v3\node_modules\next\dist\compiled\next-server\app-page.runtime.dev.js:41:1251)
    at async doRender (F:\lng-v3\node_modules\next\dist\server\base-server.js:1438:30)
    at async cacheEntry.responseCache.get.routeKind (F:\lng-v3\node_modules\next\dist\server\base-server.js:1599:28)
    at async DevServer.renderToResponseWithComponentsImpl (F:\lng-v3\node_modules\next\dist\server\base-server.js:1507:28)
    at async DevServer.renderPageComponent (F:\lng-v3\node_modules\next\dist\server\base-server.js:1931:24)
    at async DevServer.renderToResponseImpl (F:\lng-v3\node_modules\next\dist\server\base-server.js:1969:32)
    at async DevServer.pipeImpl (F:\lng-v3\node_modules\next\dist\server\base-server.js:920:25)
    at async NextNodeServer.handleCatchallRenderRequest (F:\lng-v3\node_modules\next\dist\server\next-server.js:272:17)
    at async DevServer.handleRequestImpl (F:\lng-v3\node_modules\next\dist\server\base-server.js:816:17)
    at async F:\lng-v3\node_modules\next\dist\server\dev\next-dev-server.js:339:20
    at async Span.traceAsyncFn (F:\lng-v3\node_modules\next\dist\trace\trace.js:154:20)
    at async DevServer.handleRequest (F:\lng-v3\node_modules\next\dist\server\dev\next-dev-server.js:336:24)
    at async invokeRender (F:\lng-v3\node_modules\next\dist\server\lib\router-server.js:174:21)
    at async handleRequest (F:\lng-v3\node_modules\next\dist\server\lib\router-server.js:353:24)
    at async requestHandlerImpl (F:\lng-v3\node_modules\next\dist\server\lib\router-server.js:377:13)
    at async Server.requestListener (F:\lng-v3\node_modules\next\dist\server\lib\start-server.js:141:13)
[auth][details]: {
  "provider": "credentials"
}

If you need any more info just let me know :)

I truly do apologise if this is a simple stupid mistake but are incredibly grateful to anyone who can offer help!

Many thanks, Louis


Solution

  • Although not a perfect fix, in the stage of development NextAuth v5 seems to be at, I've implemented the following fix in my login.ts server action in the switch case clause to return the correct errors.

    switch (error.type) {
        case "CredentialsSignin":
        case "CallbackRouteError":
          return { error: "Invalid credentials!" };
        default:
          return { error: "Something went wrong!" };
      }
    

    The CallbackRouteError seems to only fire when the credentials are invalid so this has been updated in my code to bypass this error.

    Hope this helps everyone :)