10

Is it possible to allow next-auth to return errors from an API and pass them through from the client-side?

As an example, the API is returning specifically if the user's email or password is incorrect. On our mobile app, this is working great. Though on the website, we're using Next-auth. Using the credentials example from the documentation, it would be great to change the return value to an object.

import CredentialsProvider from "next-auth/providers/credentials"
providers: [
  CredentialsProvider({
    name: "Credentials",
 
    credentials: {
      username: { label: "Username", type: "text", placeholder: "jsmith" },
      password: {  label: "Password", type: "password" }
    },
    async authorize(credentials, req) {
      const user = { id: 1, name: "J Smith", email: "[email protected]" }

      if (user) {
        // Any object returned will be saved in `user` property of the JWT
        return user
      } else {
        // Return an object that will pass error information through to the client-side.
        return { errors: user.errors, status: false }
      }
    }
  })
]

Please do let me know if there is another post that relates to this one as I'm unable to find it online.

4 Answers 4

20

Yes this should allow you to do that, though in the credential, when passing the values to the next auth, add a redirect:false

You can find the documentation here

Your sign in method will therefore look like this.

signIn('credentials', { redirect: false, username:'username', password: 'password' })

Your code can look like this

import CredentialsProvider from "next-auth/providers/credentials"
providers: [
  CredentialsProvider({
    name: "Credentials",
 
    credentials: {
      username: { label: "Username", type: "text", placeholder: "jsmith" },
      password: {  label: "Password", type: "password" }
    },
    async authorize(credentials, req) {
      const user = { id: 1, name: "J Smith", email: "[email protected]" }

      if (user) {
        // Any object returned will be saved in `user` property of the JWT
        return user
      } else {
        // Return an object that will pass error information through to the client-side.
        throw new Error( JSON.stringify({ errors: user.errors, status: false }))
      }
    }
  })
Sign up to request clarification or add additional context in comments.

3 Comments

This seems to have worked a treat. Thanks!
is there a better way?
Works like a charm. Thank you @obed-amoasi
1

I managed to return the correct error information by using a promise with a catch statement in the authorize function:

export const authOptions: NextAuthOptions = {
  session: {
    strategy: "jwt",
  },
  secret: process.env.NEXTAUTH_SECRET,
  providers: [
    CredentialsProvider({
      name: "credentials",
      credentials: {
        email: {
          label: "Email",
          type: "email",
          placeholder: "[email protected]",
        },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials: any) {
        // console.log(credentials);
        const { user, jwt } = await axios
          .post(`http://localhost:1337/api/auth/local`, {
            identifier: credentials.email,
            password: credentials.password,
          })
          .then(({ data }) => {
            return data;
          })
          .catch((error) => {
            throw new Error(JSON.stringify(error.response.data));
          });

        return { jwt, ...user };
      },
    }),
  ],
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

The next-auth error is of type string, so you need to stringify it as JSON, then turn it back into an object from the client side.

Comments

0

I use v5.0.0-beta.25 and the other answers did not work for me, managed to pass custom messages like this:

  1. Define the following error class
import NextAuth, { AuthError } from 'next-auth';

class InvalidCredentials extends AuthError {
  public readonly kind = 'signIn';

  constructor(message: string) {
    super(message);
    this.type = 'CredentialsSignin';
  }
}
  1. In the authorize callback, throw the error and add '|||' (some string indicator) to the message
if (user) {
    throw new InvalidCredentials('User exists|||');
}

The string indicator is used because the auth js package adds a 'read more at url xyz...' string to the message when we catch it.

  1. In the method that calls the signIn method, catch the error and you can use the custom message by splitting the error message by the string indicator.
try {
   await signIn(provider, {
      ...formData,
      redirect: false,
      type: LoginType.SIGN_UP,
   });
   success = true;
} catch (e: any) {
    return {
      error: e.message.split('|||')[0],
   };
}

error message with added next auth string

custom message after selecting the relevant msg

Comments

0
(auth.ts) you can simply use CredentialsSignin() in catch section

 authorize: async ({ email, password }) => 
{
                try {

                    const user = await prisma.user.findUnique({ where: { email: String(email) } })

                    if (!user) throw new Error('User does not exists')
                    const isValidPassword = await compare(String(password), user.password)

                    if (!isValidPassword) throw new Error('Password is wrong')
                    return { id: String(user.id), name: user.name, email: user.email }

                } catch (error: any) {
                    throw new CredentialsSignin({ cause: error.message })
                }




(serverAction.ts) in this you have to provide error type as CredentialsSignin

export const handleSignIn = async (formData: { email: string, password: string }) => 
{
    try {
        await signIn('credentials', formData)
    } catch (error) {
        const err = error as CredentialsSignin
        throw new Error(String(err.cause))
    }
}


// now you can use try catch in client for error handling 

//it is working for me

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.