Jamie Hunt
Jamie Hunt

Reputation: 416

Return error information from API when using Next-auth

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.

Upvotes: 9

Views: 14270

Answers (3)

Oren Sayag
Oren Sayag

Reputation: 41

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

Upvotes: 0

BerenBoden
BerenBoden

Reputation: 81

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.

Upvotes: 1

Obed Amoasi
Obed Amoasi

Reputation: 2039

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 }))
      }
    }
  })

Upvotes: 19

Related Questions