NextAuth v4登录失败重定向异常问题求助
我在全栈JS课程的最终项目中使用NextAuth v4.18.8(课程采用v3版本)开发登录页面,输入正确密码时可正常跳转至http://127.0.0.1:3000/user/dashboard;但输入错误密码时,未按预期跳转到/auth/signin?i=1,反而被重定向至http://127.0.0.1:3000/api/auth/error?error=Request%20failed%20with%20status%20code%20401,控制台显示POST http://localhost:3000/api/auth/callback/credentials? 401 (Unauthorized)错误。
排查发现:输入错误密码时,后端signin控制器返回res.status(401).json({ success: false, message: "invalid" }),此时axios请求会直接抛出异常,导致前端[...nextauth].js中CredentialsProvider的authorize函数内,判断用户不存在时抛出'/auth/signin?i=1'的代码无法执行。
相关代码
前端:登录页面
const handleFormSubmit = async values => { signIn('credentials', { email: values.email, password: values.password, callbackUrl: 'http://127.0.0.1:3000/user/dashboard' }) }
前端:[...nextauth].js
export const authOptions = { providers: [ CredentialsProvider({ name: 'credentials', async authorize(credentials, req) { const res = await axios.post('http://127.0.0.1:3000/api/auth/signin', credentials) const user = res.data if (user) { return user } else { throw '/auth/signin?i=1' } } }) ], session: { jwt: true }, jwt: { secret: process.env.JWT_TOKEN }, adapter: MongooseAdapter(process.env.MONGODB_URI) } export default NextAuth(authOptions)
后端:signin.js控制器
const authSignin = { post: async (req, res) => { const { name, email, password, } = req.body await dbConnect() const user = await UsersModel.findOne({ email }) if (!user) { return res.status(401).json({ success: false, message: "invalid" }) } const passIsCorrect = await compare(password, user.password) if (passIsCorrect) { return res.status(200).json({ _id: user._id, name: user.name, email: user.email }) } return res.status(401).json({ success: false, message: "invalid" }) } } export { authSignin }
后端:signin.js路由(使用Next Connect)
import nextConnect from 'next-connect' import { authSignin } from '../../../src/controllers/auth/signin' const route = nextConnect() route.post(authSignin.post) export default route
解决方法
问题核心是axios遇到401状态码会直接抛出异常,导致authorize函数的错误分支代码无法执行。提供两种解决方案:
方案1:捕获axios异常并手动抛出跳转路径
修改[...nextauth].js中的authorize函数,添加异常捕获逻辑:
async authorize(credentials, req) { try { const res = await axios.post('http://127.0.0.1:3000/api/auth/signin', credentials) const user = res.data if (user) { return user } else { throw '/auth/signin?i=1' } } catch (error) { // 捕获401异常,抛出指定跳转路径 if (error.response?.status === 401) { throw '/auth/signin?i=1' } // 其他异常重新抛出,保留原有错误逻辑 throw error } }
方案2:调整后端接口返回状态码
修改后端signin控制器,将验证失败时的401状态码改为200,通过success字段标识结果:
// 替换原401返回逻辑 if (!user) { return res.status(200).json({ success: false, message: "invalid" }) } // ... return res.status(200).json({ success: false, message: "invalid" })
同时修改前端authorize函数的判断逻辑:
async authorize(credentials, req) { const res = await axios.post('http://127.0.0.1:3000/api/auth/signin', credentials) const user = res.data if (user.success) { return { _id: user._id, name: user.name, email: user.email } } else { throw '/auth/signin?i=1' } }
两种方案任选其一即可,方案1更符合HTTP状态码语义规范,推荐使用。
内容的提问来源于stack exchange,提问作者João Textor

