'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.

401 thrown in console

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