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

NextAuth服务端调用signIn报window未定义错误及解决方案咨询

问题解答

核心结论

你不能在服务端直接调用signIn()——这个方法是next-auth/react提供的客户端专属方法,内部依赖浏览器的window对象,服务端环境没有这个对象,所以会抛出ReferenceError: window is not defined错误。

两种可行的优化方案

方案1:注册成功后由客户端触发登录(简单易实现)

API路由只负责完成用户创建,前端收到成功响应后,再调用客户端的signIn()完成会话创建和跳转。

修改后的API路由代码:

import prisma from "@/libs/client";
import bcrypt from "bcrypt"; // 需先安装bcrypt,用于加密密码

export default async function handler(req, res) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method Not Allowed" });
  }

  const { email, password, confirmPassword } = req.body;

  if (!email || !password || !confirmPassword) {
    return res.status(400).json({ error: "表单未填写完整" });
  }

  if (password !== confirmPassword) {
    return res.status(400).json({ error: "密码不匹配" });
  }

  // 先检查用户是否已存在
  const existingUser = await prisma.appUsers.findUnique({ where: { email } });
  if (existingUser) {
    return res.status(409).json({ error: "该邮箱已注册" });
  }

  // 加密密码(必须操作,禁止存储明文密码)
  const hashedPassword = await bcrypt.hash(password, 10);

  try {
    await prisma.appUsers.create({
      data: {
        email,
        password: hashedPassword,
      },
    });
    // 返回成功信息,让前端触发登录流程
    return res.status(201).json({ success: true, email });
  } catch (error) {
    return res.status(500).json({ error: "注册失败,请重试" });
  }
}

前端注册组件示例:

import { useState } from "react";
import { signIn } from "next-auth/react";

export default function RegisterForm() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [error, setError] = useState("");

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError("");

    const response = await fetch("/api/register", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, password, confirmPassword }),
    });

    const data = await response.json();
    if (!response.ok) {
      setError(data.error);
      return;
    }

    // 注册成功后调用客户端signIn,完成登录并跳转
    const signInResult = await signIn("credentials", {
      email,
      password,
      callbackUrl: "/app/feed",
      redirect: true, // 自动跳转到目标页面
    });

    if (signInResult?.error) {
      setError("登录失败,请重试");
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="邮箱"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="密码"
        required
      />
      <input
        type="password"
        value={confirmPassword}
        onChange={(e) => setConfirmPassword(e.target.value)}
        placeholder="确认密码"
        required
      />
      {error && <p style={{ color: "red" }}>{error}</p>}
      <button type="submit">注册</button>
    </form>
  );
}

方案2:服务端直接创建会话(无需客户端二次请求,更流畅)

利用NextAuth的服务端工具手动创建用户会话,然后直接重定向到目标页面,需要依赖你的NextAuth配置。

修改后的API路由代码:

import prisma from "@/libs/client";
import bcrypt from "bcrypt";
import { authOptions } from "@/pages/api/auth/[...nextauth]"; // 导入你的NextAuth配置
import { createSession } from "next-auth/core";

export default async function handler(req, res) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method Not Allowed" });
  }

  const { email, password, confirmPassword } = req.body;

  if (!email || !password || !confirmPassword) {
    return res.status(400).json({ error: "表单未填写完整" });
  }

  if (password !== confirmPassword) {
    return res.status(400).json({ error: "密码不匹配" });
  }

  const existingUser = await prisma.appUsers.findUnique({ where: { email } });
  if (existingUser) {
    return res.status(409).json({ error: "该邮箱已注册" });
  }

  const hashedPassword = await bcrypt.hash(password, 10);
  const user = await prisma.appUsers.create({
    data: {
      email,
      password: hashedPassword,
    },
  });

  // 手动创建NextAuth会话
  const session = await createSession({
    session: {
      user: {
        id: user.id,
        email: user.email,
        // 按需添加其他用户字段,需与NextAuth配置一致
      },
      expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), // 30天有效期
    },
    secret: authOptions.secret,
  });

  // 设置会话Cookie(需与NextAuth的Cookie配置匹配)
  res.setHeader(
    "Set-Cookie",
    `${authOptions.cookies.sessionToken.name}=${session.sessionToken}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${30 * 24 * 60 * 60}`
  );

  // 重定向到目标页面
  return res.redirect(302, "/app/feed");
}

重要安全提醒

  1. 禁止存储明文密码:上述代码均使用bcrypt加密密码,你的原代码直接存储明文密码是严重安全漏洞,必须修复。
  2. 确保NextAuth的credentials provider验证逻辑使用bcrypt.compare比对密码,示例如下:
// pages/api/auth/[...nextauth].js 中的credentials配置
providers: [
  CredentialsProvider({
    async authorize(credentials) {
      const user = await prisma.appUsers.findUnique({ where: { email: credentials.email } });
      if (!user) return null;
      const isPasswordValid = await bcrypt.compare(credentials.password, user.password);
      return isPasswordValid ? user : null;
    },
  }),
],

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:43:19