Reputation: 416
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
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:
import NextAuth, { AuthError } from 'next-auth';
class InvalidCredentials extends AuthError {
public readonly kind = 'signIn';
constructor(message: string) {
super(message);
this.type = 'CredentialsSignin';
}
}
authorize
callback, throw the error and add '|||' (some string indicator) to the messageif (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.
try {
await signIn(provider, {
...formData,
redirect: false,
type: LoginType.SIGN_UP,
});
success = true;
} catch (e: any) {
return {
error: e.message.split('|||')[0],
};
}
Upvotes: 0
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
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