You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13.2.4中next-auth 4.22.0的SignIn函数JSON解析错误求助

问题:NextAuth登录时返回HTML而非JSON导致语法错误

我在项目中使用Next.js 13.2.4,采用next-auth 4.22.0实现身份验证。提交登录表单时,调用signIn函数出现如下错误:

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

相关代码

/api/auth/[...nextauth]/route.js

import { PrismaClient } from "@prisma/client";
import { compare } from "bcryptjs";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
const prisma = new PrismaClient();
export const authOptions = {
  session: {
    strategy: "jwt",
  },
  providers: [
    CredentialsProvider({
      type: "credentials",
      id: "login_id",
      name: "Sign In",

      async authorize(credentials) {
        const user = {
          id: "1",
          password: "hicham",
          email: "test@gmail.com",
          name: "hicham zak",
        };
        if (!credentials?.email || !credentials.password) {
          return null;
        }

        // test
        if (!user || credentials.password !== user.password) {
          return null;
        }

        return {
          id: user.id,
          email: user.email,
          name: user.name,
          randomKey: "Hey cool",
        };
      },
    }),
  ],
  
    jwt: ({ token, user }) => {
      console.log("JWT Callback", { token, user });
      if (user) {
        const u = user;
        return {
          ...token,
          id: u.id,
          randomKey: u.randomKey,
        };
      }
      return token;
    },
  },
  pages: {
    signIn: "/login",
  },
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

LoginForm.jsx

"use client";
import Label from "../ui/Label";
import Input from "../ui/Input";
import Link from "next/link";
import Btn from "../ui/Btn";
import { useState } from "react";
import ShowHidePass from "../ShowHidePass";
import { useFormik } from "formik";
import { loginValidate } from "../../lib/validate";
import { signIn } from "next-auth/react";
import { useSearchParams, useRouter } from "next/navigation";
import Alert from "../ui/Alert";

export const contentType = "application/json";

const Form = ({ btn }) => {
  // this for formik
  const formik = useFormik({
    initialValues: {
      email: "",
      password: "",
    },
    validate: loginValidate,
    onSubmit,
  });
  const router = useRouter();
  const searchParams = useSearchParams();
  const callbackUrl = searchParams.get("callbackUrl") || "/home";

  // states
  const [error, setError] = useState("");
  // this for hide and show the password
  const [showPass, setShowPass] = useState(false);
  function handelShowPass(e) {
    e.preventDefault();
    setShowPass(!showPass);
  }
  // this function excute in submission
  async function onSubmit(values) {
    try {
      const result = await signIn("login_id", {
        email: "test@gmail.com",
        password: "test123455",
        redirect: false,
        callbackUrl,
      });
      console.log(result);
      if (!result?.error) router.push(callbackUrl);
      else setError("Invalid email or password");
    } catch (err) {
      console.log(err);
    }
  }
  return (
    <form
      method="post"
      className="space-y-8 sm1:w-[400px]"
      onSubmit={formik.handleSubmit}
    >
      <div className="grid w-full items-center gap-1.5">
        <Label htmlFor="email" text="Email" />

        <Input
          name="email"
          type="email"
          placeholder="Enter your email"
          formik={formik.getFieldProps("email")}
          style={`
            ${formik.errors.email && formik.touched.email ? "border-red-600 " : ""}
            hover:border-b-primary hover:border-b-2 hover:rounded-none hover:rounded-b-md
          `}
        />
      </div>

      <div className="grid w-full items-center gap-1.5">
        <Label htmlFor="pass" text="Password" />
        <Input
          name="password"
          type={showPass ? "text" : "password"}
          placeholder="Enter your Password"
          children={
            <ShowHidePass showPass={showPass} handelShowPass={handelShowPass} />
          }
          formik={formik.getFieldProps("password")}
          style={`
            ${formik.errors.password && formik.touched.password ? "border-red-600 " : ""}
            hover:border-b-primary hover:border-b-2 hover:rounded-none hover:rounded-b-md
            `}
        />
      </div>
      {error && <Alert children={error} />}
      <div className="w-full text-center pb-2 font-semibold text-sm">
        <Link href="/forgot-password" className="text-indigo-500">
          Forgot password?
        </Link>
      </div>

      <div className="w-full">
        <Btn btnText={btn} type="submit" />
      </div>

      <div className="w-full text-center pb-2 font-semibold text-sm">
        <span>Create an account? </span>
        <Link href="/signUp" className="text-indigo-500 hover:underline">
          Sign Up
        </Link>
      </div>
    </form>
  );
};

export default Form;

登录页面

import Form from "../../../../components/form/LoginForm";
import Logo from "../../../../components/Logo";

const Login = () => {
  return (
    <div className="h-screen w-screen sm1:flex sm1:justify-center sm1:items-center ">
      <div className="sm1:shadow-xl bg-slate-200 sm1:bg-white sm1:rounded-lg px-8 pt-12 pb-8 space-y-12">
        <Logo />
        <h1 className="text-2xl my-8 font-semibold">Login to your account</h1>
        <Form btn="Login" />
      </div>
    </div>
  );
};
export default Login;

问题原因及解决方案

这个错误是因为NextAuth的API路由返回了HTML页面(通常是错误页面)而非预期的JSON响应,以下是具体问题和修复步骤:

1. 修复NextAuth路由的语法错误

查看/api/auth/[...nextauth]/route.js代码,发现jwt配置的括号存在语法错误:jwt属性前多了一个闭合大括号,导致authOptions对象结构异常,服务器返回500错误页面(HTML)。

修改后的authOptions结构:

export const authOptions = {
  session: {
    strategy: "jwt",
  },
  providers: [
    // 保持原有provider代码不变
  ],
  // 修正:jwt直接作为authOptions的属性,移除多余的闭合括号
  jwt: ({ token, user }) => {
    console.log("JWT Callback", { token, user });
    if (user) {
      const u = user;
      return {
        ...token,
        id: u.id,
        randomKey: u.randomKey,
      };
    }
    return token;
  },
  pages: {
    signIn: "/login",
  },
};

2. 修正登录凭证不匹配问题

authorize函数中硬编码的用户密码是"hicham",但signIn调用中传入的密码是"test123455",这会导致authorize返回null,触发NextAuth重定向到登录页面(HTML)。

修改onSubmit中的密码为正确值:

async function onSubmit(values) {
  try {
    const result = await signIn("login_id", {
      email: "test@gmail.com",
      password: "hicham", // 改为和authorize中一致的密码
      redirect: false,
      callbackUrl,
    });
    // 保持后续代码不变
  } catch (err) {
    console.log(err);
  }
}

3. 移除表单的method="post"属性

客户端组件中使用signIn函数时,不需要给form标签设置method="post",否则会触发默认表单提交行为,导致请求错误。

修改LoginForm中的form标签:

<form
  className="space-y-8 sm1:w-[400px]"
  onSubmit={formik.handleSubmit}
>

4. 验证NextAuth路由路径是否正确

确保NextAuth的API路由文件放在app/api/auth/[...nextauth]/route.js(App Router结构),如果是Pages Router则路径应为pages/api/auth/[...nextauth].js,路径错误会返回404页面(HTML)。

完成以上修改后,重启开发服务器,登录请求会返回正确的JSON响应,解决解析错误。

内容的提问来源于stack exchange,提问作者Hicham Zakroum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 02:54:53