'Why do I get a 401 unauthorized error when logging in?
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
}
})
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|