next-auth icon indicating copy to clipboard operation
next-auth copied to clipboard

Cannot return custom error to the client side in the Next-Auth v5 Credentials Auth

Open dsmabulage opened this issue 1 year ago • 7 comments

Provider type

Credentials

Environment

 "next": "14.2.3",
 "next-auth": "^5.0.0-beta.19",

Reproduction URL

https://github.com/dsmabulage/inventes

Describe the issue

I'm trying to implement the email, password login in the next app using next-auth v5.

In the NextAuth function inside authorize, I tried various methods to throw a custom error message that can passed to the user to show what's going wrong with the credentials.

How to reproduce

export const { auth, handlers, signIn, signOut } = NextAuth({
  session: {
    strategy: 'jwt',
  },
  pages: {
    signIn: '/auth/login',
  },
  providers: [
    CredentialsProvider({
      credentials: {
        email: {},
        password: {},
      },
      async authorize({ email, password }) {
        try {
          throw new Error(
            JSON.stringify({ errors: 'user.errors', status: false })
          );
        } catch (error) {
          throw new Error(
            JSON.stringify({ errors: ' catch user.errors', status: false })
          );
        }
      },
    }),
  ],

Expected behavior

I want to get that error message to the client side and show that error message to the user.

dsmabulage avatar Jun 17 '24 14:06 dsmabulage

Hi, I'm facing the same problem. I found this temporary solution that may be helpful: https://github.com/nextauthjs/next-auth/issues/9900#issuecomment-2167681517.

hezean avatar Jun 18 '24 05:06 hezean

The same issue is being discussed here in detail. I might also tell you that there is a way to check for some error but that also seems to broken currently In previous versions you could do something like this:

import { AuthError } from "next-auth";
import { signIn } from "next-auth/react";
    try {
      setLoading(true);
      setError("");
      const res = await signIn("credentials", {
        redirect: false,
        email,
        password,
      });

      //* The res here has no credentials data only error:true or error:null so we can manage the state based on that
      //* Next auth does not send the data due to security reasons
      if (res.error === null) {
        router.push("/protected");
      }
      if (res.error) {
        setError("Check Your Email Or Password");
      }
    } catch (error) {
      console.error(error.message);
      if (error instanceof AuthError) {
        switch (error.type) {
          case "CredentialsSignin":
            console.log("Invalid Password");
            setError("Please Check Your Password")
          default:
            console.log("Something went wrong");
            setError("Something went wrong")
        }
      }
    } finally {
      setLoading(false);
    }
  };

The issue with this approach that you do not know if the error is related to credentials like invalid email or something like database connection timeout

Ali-Raza764 avatar Jun 21 '24 06:06 Ali-Raza764

The same issue is being discussed here in detail. I might also tell you that there is a way to check for some error but that also seems to broken currently In previous versions you could do something like this:

import { AuthError } from "next-auth";
import { signIn } from "next-auth/react";
    try {
      setLoading(true);
      setError("");
      const res = await signIn("credentials", {
        redirect: false,
        email,
        password,
      });

      //* The res here has no credentials data only error:true or error:null so we can manage the state based on that
      //* Next auth does not send the data due to security reasons
      if (res.error === null) {
        router.push("/protected");
      }
      if (res.error) {
        setError("Check Your Email Or Password");
      }
    } catch (error) {
      console.error(error.message);
      if (error instanceof AuthError) {
        switch (error.type) {
          case "CredentialsSignin":
            console.log("Invalid Password");
            setError("Please Check Your Password")
          default:
            console.log("Something went wrong");
            setError("Something went wrong")
        }
      }
    } finally {
      setLoading(false);
    }
  };

The issue with this approach that you do not know if the error is related to credentials like invalid email or something like database connection timeout

Is there a way to throw a custom error message from the authorize method because I have another validation more than checking only the username and password? So that case I need to show different error messages to the client.

dsmabulage avatar Jun 21 '24 08:06 dsmabulage

we can customize the authorize() function to return custom errors like this https://github.com/nextauthjs/next-auth/pull/9871 And here https://github.com/nextauthjs/next-auth/issues/9099 But to be honest I am not sure how to implement these.

Ali-Raza764 avatar Jun 21 '24 09:06 Ali-Raza764

I read somewhere one should be able to throw a new Error with a custom code property (e.g. {code: 'Not verified.'}) and it will be passed to the client. However in 5.0.0-beta.19 it will always return null, {error: 'Configuration', code: null, status: 200, ok: true, url: null}

douwepausma avatar Jun 24 '24 07:06 douwepausma

I read somewhere one should be able to throw a new Error with a custom code property (e.g. {code: 'Not verified.'}) and it will be passed to the client. However in 5.0.0-beta.19 it will always return null, {error: 'Configuration', code: null, status: 200, ok: true, url: null}

always return null, is it a next-auth issue

dsmabulage avatar Jun 24 '24 14:06 dsmabulage

https://github.com/nextauthjs/next-auth/issues/11074#issuecomment-2184315761 Follow here for discussions.

Ali-Raza764 avatar Jun 25 '24 10:06 Ali-Raza764

Same here I always get these return object even I explicity return an error

image

image

RuentDev avatar Jul 06 '24 02:07 RuentDev

https://github.com/nextauthjs/next-auth/issues/11074#issuecomment-2205152161 Please follow this issue for details

Ali-Raza764 avatar Jul 06 '24 06:07 Ali-Raza764

This is how i was able to revolve this

In auth.ts or auth.config.ts

create a class that extend theAuthError class

class InvalidLoginError extends AuthError {
  code = 'custom';
  errorMessage: string;
  constructor(message?: any, errorOptions?: any) {
    super(message, errorOptions);
    this.errorMessage = message;
  }
}

then in your credential provider you can use it like

providers: [
    Credentials({
      async authorize(credentials) {
        try {
          const response = await axios.post('your-endpoint', {
            phone: credentials?.phone,
            password: credentials?.password,
          });
          if (response.data.error) {
            throw new InvalidLoginError(response.data.error);
          }

          const user = response?.data?.data;
          if (user) {
            return user;
          } else {
            return null;
          }
        } catch (e: any) {
          throw new InvalidLoginError(e.response?.data?.message);
        }
      },
    }),
  ],

with this, you can catch the error in your signin api route and also your client signin component

OlushesiToheeb avatar Jul 21 '24 15:07 OlushesiToheeb

I was able to fix this problem check out in discussions https://github.com/nextauthjs/next-auth/discussions/8999#discussioncomment-10107786

aaliboyev avatar Jul 21 '24 17:07 aaliboyev

Fixed in #11469

balazsorban44 avatar Jul 27 '24 15:07 balazsorban44