I am using the NextAuth.js credentials provider for the log in procedure. When logging in I am catching the errors in a try-catch block and set the error state accordingly, but I do not display the error state anywhere yet. Even though I am catching the errors, a 401 unauthorized gets thrown when trying to log in. I am using wrong credentials, thus expecting an error called CredentialsSignin which I am getting, but additionally I am getting the 401 every time. The problem is that I am not able to detect where it is thrown, that might be the reason I am not able to handle it.
Here the code of my custom log in page:
import { InferGetServerSidePropsType } from "next"
import { CtxOrReq } from "next-auth/client/_utils";
import { getCsrfToken, getSession, signIn } from "next-auth/react"
import { useRouter } from "next/router";
import { useCallback, useEffect, useState } from "react";
import { useFormik } from "formik"
import * as Yup from "yup"
export default function Login({ csrfToken }: InferGetServerSidePropsType<typeof getServerSideProps>) {
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const router = useRouter()
type Credentials = {
email: string
password: string
}
let credentials: Credentials;
const handleLogin = useCallback(async (credentials) => {
if (!credentials.email) {
return setError('email is missing')
}
if (!credentials.password) {
return setError('password is missing')
}
try {
setLoading(true)
const response: any = await signIn('credentials', { ...credentials, redirect: false }) // find right type
if (response.error && response.error === 'CredentialsSignin') {
setError('email or password are wrong')
} else {
setError('')
router.push('/')
}
} catch {
setError('login failed')
} finally {
setLoading(false)
}
}, [router])
const formik = useFormik({
initialValues: {
email: "",
password: ""
},
validationSchema: Yup.object({
email: Yup.string()
.required("email address is required"),
password: Yup.string()
.required("password is required")
}),
onSubmit: values => {
credentials = {
email: values.email,
password: values.password
}
handleLogin(credentials)
}
})
return (
<form onSubmit={formik.handleSubmit} noValidate>
<input name="csrfToken" type="hidden" defaultValue={csrfToken} />
<label>
Email
<input
name="email"
type="email"
value={formik.values.email}
onChange={formik.handleChange}
onBlur={formik.handleBlur} />
</label>
{formik.touched.email && formik.errors.email && <p>{formik.errors.email}</p>}
<label>
Password
<input
name="password"
type="password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur} />
</label>
{formik.touched.password && formik.errors.password && <p>{formik.errors.password}</p>}
<button type="submit">Login</button>
</form>
)
}
export async function getServerSideProps(context: CtxOrReq | undefined) {
const session = await getSession(context)
if (session) {
return {
redirect: { destination: '/' }
}
}
return {
props: {
csrfToken: await getCsrfToken(context)
},
}
}
Here the code of my [...nextauth].ts API page:
import NextAuth from 'next-auth'
import { PrismaAdapter } from '@next-auth/prisma-adapter'
import { prisma } from '../../../prisma/prisma_client'
import CredentialsProvider from "next-auth/providers/credentials"
import { compare } from 'bcryptjs'
export default NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: {},
password: {}
},
async authorize(credentials) {
if (!credentials) {
return null
}
const { email } = credentials
const { password } = credentials
const storedUser = await prisma.user.findUnique({
where: {
email
}, select: {
id: true,
email: true,
hashedPassword: true,
company: {
select: {
id: true,
name: true
}
}
}
})
if (!storedUser) {
return null
}
const user = {
id: storedUser?.id,
email,
comanyId: storedUser?.company?.id,
companyName: storedUser?.company?.name,
}
const validatePassword = await compare(password, storedUser.hashedPassword)
return validatePassword ? user : null
}
})
],
pages: {
signIn: '/login'
},
callbacks: {
async jwt({ token, user }) {
user && (token.user = user)
return token
},
async session({ session, token }) {
session.user = token.user
return session
}
},
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60 // 30 days
}
})
If you are running on production other than the master/main branch then the issue might be due to the vercel authorization on preview branches. Disabling it on the "dashboard=>settings=>deployment-protection" could solve the issue.
Please make sure, that you set 'withCredentials' to 'true' for your API object
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With