next.js 为什么登录时出现401未授权错误?

oxosxuxt  于 2022-11-29  发布在  其他
关注(0)|答案(2)|浏览(199)

我在登录过程中使用NextAuth.js凭据提供程序。登录时,我在try-catch块中捕获错误,并相应地设置错误状态,但我还没有在任何地方显示错误状态。即使我捕获了错误,在尝试登录时仍会抛出401 unauthorized。我使用了错误的凭据,因此预期会出现名为CredentialsSignin的错误,但我得到了该错误。但另外我每次都得到401。问题是我无法检测到它被扔到哪里,这可能是我无法处理它的原因。

下面是我的自定义登录页面的代码:

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)
        },
    }
}

下面是我的[... nextauth].ts API页面的代码:

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
  }
})
uklbhaso

uklbhaso1#

请确保将API对象的“withCredentials”设置为“true”

fwzugrvs

fwzugrvs2#

遇到了同样的问题,并设法解决了它。下面的第一个代码块是有错误的版本,第二个代码块是工作版本。
因此,有几件事需要注意:

  • 我为catchresponse返回了相同的数据,因为我想确定问题是否是由返回的数据引起的。但事实并非如此,所以在工作版本中恢复了这一点。
  • 在工作版本中,promise有return,promise中有return。我的理解是,responsecatch块本身就是函数,在内部块中返回不会传播值,从而由authorize函数返回。换句话说,我什么也不返回--结果是401
  • 基于同样的理解,OP代码的问题是它总是无法返回用户对象。如果不运行代码,我不能确定它是什么,但有几个可能的路径导致这样。

第一个

相关问题