如何在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
相关产品推荐
相关产品推荐

