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

自定义NextAuth登录表单触发[CLIENT_FETCH_ERROR]问题求助

NextAuth自定义登录页调用signIn出现CLIENT_FETCH_ERROR问题

问题描述

使用NextAuth搭建自定义登录页面时,调用signIn函数触发以下错误:

[next-auth][error][CLIENT_FETCH_ERROR], https://next-auth.js.org/errors#client_fetch_error Failed to fetch {error: {…}, url: '/api/auth/csrf', message: 'Failed to fetch'

已配置NEXTAUTH_URL但问题未解决,无法通过表单完成登录。不过NextAuth默认登录表单功能正常,检查NextAuthOptions配置未发现明显遗漏。

用户提供的配置信息如下:

package.json

{"scripts": {"dev": "next dev","build": "next build","start": "next start","lint": "next lint"},"dependencies": {"next": "^13.4.0","next-auth": "^4.22.1","react": "^18.2.0","react-dom": "^18.2.0","react-icons": "^4.8.0","react-quill": "^2.0.0","sass": "^1.62.1","zod": "^3.21.4"},"devDependencies": {"@types/node": "20.0.0","@types/react": "18.2.5","typescript": "5.0.4"}}

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

import NextAuth, { type NextAuthOptions } from "next-auth";import CredentialsProvider from "next-auth/providers/credentials";export const authOptions: NextAuthOptions = {secret: process.env.NEXT_PUBLIC_SECRET,providers: [CredentialsProvider({name: "Credentials",credentials: {username: { label: "username", type: "text", placeholder: "jsmith" },password: { label: "Password", type: "password" }},async authorize(credentials, req) {console.log("credentials", credentials);const user = { id: "1", name: "J Smith", email: "jsmith@example.com" };console.log("user", user);if (user) return user;else return null;}})],session: {strategy: 'jwt'},pages: {signIn: "/auth/signIn",},debug: true,};const handler = NextAuth(authOptions);export { handler as GET, handler as POST };

auth/signIn/page.tsx

'use client';import React from "react";import { signIn, useSession } from "next-auth/react";import styles from "./page.module.scss";import { redirect } from "next/navigation";export default function LoginPage() {const userNameRef = React.useRef<HTMLInputElement>(null);const passwordRef = React.useRef<HTMLInputElement>(null);const session = useSession();React.useEffect(() => {if (session.status === "authenticated") redirect("/admin");}, [session]);const onSubmit = async () => {const result = signIn("Credentials", { username: "jsmith", password: "1234" })console.log("value", result);}return (<div className={styles.login}><form className={styles.login__form}><h1 className={styles.login__form__title}>Login</h1><div className={styles.login__form__label__input}><label className={styles.login__form__label}>Username</label><input ref={userNameRef} className={styles.login__form__input} type="text"></input></div><div className={styles.login__form__label__input}><label className={styles.login__form__label}>Password</label><input ref={passwordRef} className={styles.login__form__input} type="password"></input></div><button onClick={onSubmit} className={styles.login__form__button}>Login</button></form></div>);}

.env

NEXT_PUBLIC_SECRET="784923748923748972938478fioswhjifuhef409283094823"NEXTAUTH_URL=http://localhost:3000

修复方案

1. 阻止表单默认提交行为

按钮点击会触发表单默认提交,导致页面刷新中断signIn异步请求,引发fetch失败。需在提交函数中阻止该行为。

2. 修正Secret环境变量配置

NextAuth的secret不能使用NEXT_PUBLIC_前缀的变量(该前缀变量会暴露给客户端,存在安全风险),需改为NEXTAUTH_SECRET。

3. 异步调用signIn时添加await

signIn是异步函数,未加await会导致无法正确获取结果,且可能引发异步流程错误。

4. 使用输入框实际值登录

原代码硬编码用户名密码,需通过ref获取输入框的真实值传入signIn。

5. 统一Provider名称大小写

signIn调用的provider名称需与配置中的CredentialsProvider内部名称一致(小写credentials)。


修改后的代码

修改后的.env

NEXTAUTH_SECRET="784923748923748972938478fioswhjifuhef409283094823"
NEXTAUTH_URL=http://localhost:3000

修改后的api/auth/[...nextauth]/route.ts

import NextAuth, { type NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";

export const authOptions: NextAuthOptions = {
  secret: process.env.NEXTAUTH_SECRET,
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        username: { label: "username", type: "text", placeholder: "jsmith" },
        password: { label: "Password", type: "password" }
      },
      async authorize(credentials, req) {
        console.log("credentials", credentials);
        // 替换为实际用户校验逻辑,以下为示例
        if (credentials?.username === "jsmith" && credentials?.password === "1234") {
          const user = { id: "1", name: "J Smith", email: "jsmith@example.com" };
          console.log("user", user);
          return user;
        }
        return null;
      }
    })
  ],
  session: {
    strategy: 'jwt'
  },
  pages: {
    signIn: "/auth/signIn",
  },
  debug: true,
};

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

修改后的auth/signIn/page.tsx

'use client';
import React from "react";
import { signIn, useSession } from "next-auth/react";
import styles from "./page.module.scss";
import { redirect } from "next/navigation";

export default function LoginPage() {
  const userNameRef = React.useRef<HTMLInputElement>(null);
  const passwordRef = React.useRef<HTMLInputElement>(null);
  const { status } = useSession();

  React.useEffect(() => {
    if (status === "authenticated") redirect("/admin");
  }, [status]);

  const onSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const username = userNameRef.current?.value || "";
    const password = passwordRef.current?.value || "";
    
    const result = await signIn("credentials", {
      username,
      password,
      redirect: false // 可选,关闭自动跳转以便自行处理结果
    });
    console.log("登录结果", result);
  }

  return (
    <div className={styles.login}>
      <form className={styles.login__form} onSubmit={onSubmit}>
        <h1 className={styles.login__form__title}>Login</h1>
        <div className={styles.login__form__label__input}>
          <label className={styles.login__form__label}>Username</label>
          <input ref={userNameRef} className={styles.login__form__input} type="text" />
        </div>
        <div className={styles.login__form__label__input}>
          <label className={styles.login__form__label}>Password</label>
          <input ref={passwordRef} className={styles.login__form__input} type="password" />
        </div>
        <button type="submit" className={styles.login__form__button}>Login</button>
      </form>
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 05:07:52