New Sep 18, 2024

can I customize the error in Next-auth (nextjs)?

Libraries, Frameworks, etc. All from Newest questions tagged reactjs - Stack Overflow View can I customize the error in Next-auth (nextjs)? on stackoverflow.com

I am trying to add authentication to my nextjs app using Next-auth (using the credentials provider) and I can't find a way to customize the error (i.e wrong password, email doesn't exist ...)

here is my auth.ts code :

import NextAuth from "next-auth"
import Credentials from "next-auth/providers/credentials";
import { PrismaAdapter } from "@auth/prisma-adapter"
import { db } from "./app/lib/db";
import bcryptjs from 'bcryptjs'

export class IncorrectPasswordError extends Error { constructor(message:string) { super(message); this.name = 'IncorrectPasswordError'; } }

interface User { id : string, name : string, username : string, email : string

}

export const { handlers, signIn, signOut, auth } = NextAuth({ secret : process.env.NEXTAUTH_SECRET, pages : { signIn: '/sign-in',

}, adapter: PrismaAdapter(db), session : { strategy: 'jwt' }, callbacks: { async jwt({ token, user }) { if (user) { token.id = user.id token.username = user?.username } return token }, async session({ session, token }) { if (session.user) { session.user.id = token.id as string session.user.username = token.username as string } return session } }, providers: [ Credentials({ // You can specify which fields should be submitted, by adding keys to the credentials object. // e.g. domain, username, password, 2FA token, etc. credentials: { email: {label:"Email",type:"email",placeholder:"jb@gmail.com"}, password: {label:"password",type:'password'}, }, authorize: async (credentials) => { if(!credentials?.email || !credentials?.password){ console.log('Missing email or password'); return null; } // try{ console.log('Attempting to authenticate user:', credentials.email); const existingUser = await db.user.findUnique({ where:{email: credentials?.email} }); if(!existingUser){ console.log('user do not exist') throw new Error('user does not exist'); return null; } const pwdMatch = await bcryptjs.compare(credentials?.password, existingUser?.password) if(!pwdMatch){ console.log('pwd does not match') return null; } const {id, email, username} = existingUser; console.log('Authentication successful for user:', email); return { id,email,username } // }catch (error) { // // Handle errors gracefully (e.g., logging, error messages) // console.error('Error during authentication:', error); // throw new Error('Authentication failed'); // Or customize error message // }

}, }), ], })

and here is my sign-in page :

'use client'

import { useState } from 'react' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import * as z from 'zod' import axios from 'axios' import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { AlertCircle, CheckCircle2 } from 'lucide-react' import { useRouter } from 'next/navigation' import { SignIn } from '@/app/lib/auth-action' import Link from 'next/link' import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form' import { toast } from 'react-toastify' import { IncorrectPasswordError } from '@/auth'

const signUpSchema = z.object({ email: z.string().min(8, 'Email is required').email('Invalid email address'), password: z.string().min(1, 'Password is required'), })

type SignUpFormData = z.infer<typeof signUpSchema>

export default function SignUpPage() { const [isLoading, setIsLoading] = useState(false) const [apiResponse, setApiResponse] = useState<{ success: boolean; message: string } | null>(null) const router = useRouter()

const form = useForm<SignUpFormData>({ resolver: zodResolver(signUpSchema), defaultValues : { email : "", password : "" } });

const {isSubmitting ,isValid} = form.formState

const onSubmit = async (data: SignUpFormData) => { try { const response = await SignIn(data); console.log('SignIn response:', response);

if (response?.error) { //handle error toast.error(response.error); } else if (response?.ok) { //handle response } } catch (error) { if (error instanceof IncorrectPasswordError ) { console.log("wrong password") } toast.error('An error occurred during sign in'); } }

return ( <div className="min-h-screen flex items-center justify-center bg-gray-100"> <Card className="w-full max-w-md"> <CardHeader> <CardTitle>Sign In</CardTitle> <CardDescription>Sign in to your account.</CardDescription> </CardHeader> <CardContent> <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className='space-y-4' > <FormField control={form.control} name="email" render={({ field }) => ( <FormItem > <FormLabel>Email</FormLabel> <FormControl > <Input type='email' placeholder="alex@gmail.com" {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="password" render={({ field }) => ( <FormItem > <FormLabel>password</FormLabel> <FormControl > <Input type='password' placeholder="Enter password" {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> <Button type="submit" className="w-full bg-violet-700 hover:bg-violet-900" disabled={isSubmitting || !isValid}> {isSubmitting ? 'Signing in...' : 'Sign in'} </Button> </form> </Form> </CardContent>

<CardFooter className='flex flex-col'> <Link href="/sign-up" className=' text-violet-700 hover:text-violet-900'>Create an account</Link> </CardFooter> </Card> </div> ) }

and here is the code of the SignIn function (I couldn't use it directly in my sign-in component since it's a client component that's why I put it in a separate file):

interface CredsProps{
    email : string,
    password: string
}

export async function SignIn(data:CredsProps){ const result = await signIn('credentials', data); return result; }

I am out of ideas lol I tried everything I could think of, so I'll let the Professionals give their solutions ;-)

Scroll to top