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

如何在Next.js后端通过NextAuth Email Provider实现用户登录?

NextAuth Email Provider 后端无重定向登录实现方案

核心思路

通过后端API触发NextAuth的Email Provider登录流程,设置redirect: false避免页面重定向,同时结合ReCAPTCHA验证、新用户判断逻辑,返回结果给前端控制页面展示。


1. 配置NextAuth基础环境

确保pages/api/auth/[...nextauth].js中Email Provider配置正确,重点支持无重定向模式:

import NextAuth from "next-auth";
import EmailProvider from "next-auth/providers/email";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import prisma from "../../../lib/prisma"; // 替换为你的数据库实例

export const authOptions = {
  adapter: PrismaAdapter(prisma),
  providers: [
    EmailProvider({
      server: process.env.EMAIL_SERVER,
      from: process.env.EMAIL_FROM,
      // 可选:自定义验证邮件发送逻辑
      async sendVerificationRequest({ identifier: email, url }) {
        // 接入自有邮件服务(如SendGrid/Mailgun)发送验证链接
        await fetch(process.env.EMAIL_SERVICE_API, {
          method: "POST",
          body: JSON.stringify({ to: email, link: url }),
        });
      },
    }),
  ],
  session: { strategy: "jwt" },
};

export default NextAuth(authOptions);

2. 创建ReCAPTCHA验证与登录触发API

新建pages/api/auth/verify-recaptcha-login.js,处理人机验证、用户存在判断、触发Email登录流程:

import { signIn } from "next-auth/react";
import { authOptions } from "./[...nextauth]";
import axios from "axios";
import prisma from "../../../lib/prisma";

export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "仅支持POST请求" });

  const { email, recaptchaToken } = req.body;

  // 1. 验证ReCAPTCHA v3 Token
  try {
    const captchaRes = await axios.post(
      "https://www.google.com/recaptcha/api/siteverify",
      null,
      {
        params: {
          secret: process.env.RECAPTCHA_SECRET_KEY,
          response: recaptchaToken,
        },
      }
    );

    if (!captchaRes.data.success) {
      return res.status(400).json({ error: "人机验证失败" });
    }
  } catch (err) {
    return res.status(500).json({ error: "人机验证服务异常" });
  }

  // 2. 判断用户是否为新用户
  const existingUser = await prisma.user.findUnique({ where: { email } });
  const isNewUser = !existingUser;

  // 3. 触发NextAuth Email登录流程(无重定向)
  try {
    const signInResult = await signIn("email", {
      email,
      redirect: false, // 关键:禁止自动重定向
      req,
      res,
    });

    if (signInResult.ok) {
      return res.json({
        success: true,
        sentVerificationEmail: true,
        isNewUser,
      });
    } else {
      return res.status(400).json({ error: signInResult.error });
    }
  } catch (err) {
    return res.status(500).json({ error: "登录流程触发失败" });
  }
}

3. 前端createOrSignIn页面逻辑

从URL参数提取邮箱,加载ReCAPTCHA并调用后端API,根据返回结果显示用户名表单:

import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { loadReCaptcha } from "react-google-recaptcha-v3";

export default function CreateOrSignIn() {
  const router = useRouter();
  const { email } = router.query;
  const [showUsernameForm, setShowUsernameForm] = useState(false);
  const [username, setUsername] = useState("");

  useEffect(() => {
    if (!email) return;

    const initAuthFlow = async () => {
      // 加载ReCAPTCHA并获取验证Token
      await loadReCaptcha(process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY);
      const recaptchaToken = await window.grecaptcha.execute(
        process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY,
        { action: "auth" }
      );

      // 调用后端API
      const res = await fetch("/api/auth/verify-recaptcha-login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, recaptchaToken }),
      });

      const data = await res.json();
      if (data.success) {
        if (data.isNewUser) {
          setShowUsernameForm(true);
        } else {
          alert("验证邮件已发送,请查收");
        }
      } else {
        alert(data.error);
        router.push("/login");
      }
    };

    initAuthFlow();
  }, [email, router]);

  const handleUsernameSubmit = async (e) => {
    e.preventDefault();
    // 调用API更新新用户的用户名
    const res = await fetch("/api/auth/update-username", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, username }),
    });

    const data = await res.json();
    if (data.success) {
      alert("用户名设置完成,验证邮件已发送");
      router.push("/");
    } else {
      alert(data.error);
    }
  };

  return (
    <div className="auth-container">
      {showUsernameForm ? (
        <form onSubmit={handleUsernameSubmit}>
          <h3>设置用户名</h3>
          <input
            type="text"
            value={username}
            onChange={(e) => setUsername(e.target.value)}
            placeholder="请输入用户名"
            required
          />
          <button type="submit">确认</button>
        </form>
      ) : (
        <p>正在验证身份,请稍候...</p>
      )}
    </div>
  );
}

4. 新增用户名更新API

新建pages/api/auth/update-username.js,用于新用户完成用户名设置:

import prisma from "../../../lib/prisma";

export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "仅支持POST请求" });

  const { email, username } = req.body;

  try {
    await prisma.user.update({
      where: { email },
      data: { name: username },
    });

    return res.json({ success: true });
  } catch (err) {
    return res.status(500).json({ error: "用户名更新失败" });
  }
}

关键注意事项

  • ReCAPTCHA密钥需区分前后端:前端用NEXT_PUBLIC_RECAPTCHA_SITE_KEY,后端用RECAPTCHA_SECRET_KEY
  • 服务器端调用signIn必须传递req和res参数,否则无法正确处理会话Cookie
  • 新用户判断逻辑依赖数据库查询,需确保数据库连接正常
  • 验证邮件的URL会自动包含NextAuth的登录回调,用户点击后即可完成登录

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 10:15:36