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

NextAuth登录时出现TypeError: fetch failed问题求助

问题描述

每次通过任意登录提供商登录时,都会触发以下错误:

[next-auth][error][CLIENT_FETCH_ERROR] 
https://next-auth.js.org/errors#client_fetch_error fetch failed {
  error: {
    message: 'fetch failed',
    stack: 'TypeError: fetch failed\n' +
      '    at Object.fetch (node:internal/deps/undici/undici:11457:11)\n' +
      '    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)',
    name: 'TypeError'
  },
  url: 'http://localhost:3000/api/auth/session',
  message: 'fetch failed'
}

登录完成后无法重定向至首页/,页面无法识别已登录状态,但直接访问http://localhost:3000/api/auth/session能看到正确的会话信息。问题出现在执行next build之后,怀疑和getServerSideProps有关。

相关配置代码

.env 文件

NEXTAUTH_URL=http://localhost:3000

EMAIL_FROM=noreply@example.com
EMAIL_SERVER=smtp:

GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=

AUTH_SECRET=myPassword

[...nextauth].js 文件

export const authOptions = {
  secret: process.env.AUTH_SECRET,
  providers: [
    CredentialsProvider({
      id: 'credentials',
      name: 'credentials',
      credentials: {
        email: { label: "E-mail", type: "text", placeholder: "youremail@mail.com" },
        password: { label: "Password", type: "password", placeholder: "password" },
      },
      authorize: async (credentials) => {
        const user = await prisma.user.findUnique({
          where: {
            email: credentials.email
          }
        });

        if (!user) return null;

        return user;

      },
    }),
    EmailProvider({
      server: process.env.EMAIL_SERVER,
      from: process.env.EMAIL_FROM
    }),
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET
    }),
    DiscordProvider({
      clientId: process.env.DISCORD_CLIENT_ID,
      clientSecret: process.env.DISCORD_CLIENT_SECRET
    }),
  ],
  adapter: PrismaAdapter(prisma),
  session: {
    strategy: 'jwt',
  },
  maxAge: 30 * 24 * 60 * 60,
}

pages/index.js 文件

import { Inter } from "@next/font/google";
import { useEffect } from "react";
import {useDispatch} from "react-redux";
import {setUserState} from "@//tools/redux/userSlice";
import { getSession, useSession, signIn, signOut } from "next-auth/react";


const inter = Inter({ subsets: ["latin"] });

export default function Home(props: any) {
  const { data: session, status } = useSession();
  const dispatch = useDispatch()

  useEffect(() => {
    (async () => {
      if (session) dispatch(setUserState(session.user));
    })()
  }, []);

  return (
      <div className="flex justify-center items-center flex-col my-32">
        {!props.user &&
          <>
            <h1 className="text-5xl font-bold text-gray-500 ">You are not logged in!</h1>
            <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full" onClick={() => signIn()}>Sign in</button>
          </>
        }
        {props.user &&
        <>
          <h1 className="text-5xl font-bold text-gray-500   ">Welcome, {props.user?.email ?? 'User'}</h1>
          <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"onClick={() => signOut()}>Sign out</button>
        </>
      }
      </div>
  );
}

export async function getServerSideProps(ctx: any) {
  const session = await getSession(ctx);
  if (!session) {
    return {
      props: {}
    };
  }
  const { user } = session;
  return {
    props: { user }
  };
}

解决方案

1. 修正环境变量配置

  • NEXTAUTH_URL:静态构建后客户端可能无法正确解析本地地址,生产构建时建议设置为实际域名,或确保启动服务时的HOST/PORT与该配置完全一致;本地测试可保留,但要注意next start启动的服务端口必须是3000。
  • AUTH_SECRET:不要用myPassword这种简单字符串,执行openssl rand -hex 32生成一个安全的随机密钥替换。

2. 修复getServerSideProps会话逻辑

当前逻辑在无会话时返回空props,容易和客户端useSession的加载状态冲突,导致页面误判登录状态。修改如下:

export async function getServerSideProps(ctx: any) {
  const session = await getSession(ctx);
  // 统一返回session对象,不管是否登录
  return {
    props: { 
      session: session || null
    }
  };
}

同时修改首页组件,统一使用服务端+客户端的会话状态,避免混用:

export default function Home({ session: serverSession }) {
  const { data: clientSession, status } = useSession({
    required: true,
    onUnauthenticated() {
      // 未登录时可添加跳转逻辑
    }
  });
  const dispatch = useDispatch()

  useEffect(() => {
    const currentSession = serverSession || clientSession;
    if (currentSession) dispatch(setUserState(currentSession.user));
  }, [serverSession, clientSession, dispatch]);

  return (
      <div className="flex justify-center items-center flex-col my-32">
        {status === 'loading' ? (
          <p>加载中...</p>
        ) : !serverSession && !clientSession ? (
          <>
            <h1 className="text-5xl font-bold text-gray-500 ">你未登录!</h1>
            <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full" onClick={() => signIn()}>登录</button>
          </>
        ) : (
          <>
            <h1 className="text-5xl font-bold text-gray-500   ">欢迎,{(serverSession || clientSession).user?.email ?? '用户'}</h1>
            <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full" onClick={() => signOut()}>退出登录</button>
          </>
        )}
      </div>
  );
}

3. 修正NextAuth会话配置

maxAge属性位置错误,应该放到session配置对象内,否则不会生效:

export const authOptions = {
  secret: process.env.AUTH_SECRET,
  providers: [/* ... */],
  adapter: PrismaAdapter(prisma),
  session: {
    strategy: 'jwt',
    maxAge: 30 * 24 * 60 * 60, // 移到此处
  },
}

4. 规范构建后服务启动

执行next build后,必须用next start启动服务,不能用next dev。启动时检查控制台输出的服务地址,确保和NEXTAUTH_URL一致。

5. 补全CredentialsProvider密码验证

当前授权逻辑只检查用户存在,未验证密码,既不安全也可能导致会话生成异常(假设用户表存储哈希密码):

import bcrypt from 'bcryptjs';

// 替换原authorize函数
authorize: async (credentials) => {
  const user = await prisma.user.findUnique({
    where: {
      email: credentials.email
    }
  });

  if (!user) return null;

  // 验证密码哈希
  const isPasswordValid = await bcrypt.compare(credentials.password, user.password);
  if (!isPasswordValid) return null;

  return user;
},

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 06:17:00