使用Next Auth+Firebase Adapter实现邮箱密码登录的问题
解决NextAuth + Firebase Adapter邮箱密码登录问题
你的核心问题是没有配置CredentialsProvider——当前代码里只有Google登录提供者,调用signIn("credentials")时NextAuth找不到对应的认证逻辑,自然不会触发任何回调或输出。以下是完整的修复方案:
步骤1:安装依赖
需要补充Firebase相关依赖:
npm install firebase-admin next-auth@latest
步骤2:初始化Firebase Admin
在项目根目录创建lib/firebase-admin.ts:
import admin from "firebase-admin"; // 避免重复初始化 if (!admin.apps.length) { admin.initializeApp({ credential: admin.credential.cert({ projectId: process.env.FIREBASE_PROJECT_ID, clientEmail: process.env.FIREBASE_CLIENT_EMAIL, privateKey: process.env.FIREBASE_PRIVATE_KEY!.replace(/\\n/g, "\n"), }), }); } export const adminAuth = admin.auth();
步骤3:修改NextAuth配置文件
替换app/api/auth/[...nextauth]/route.ts的内容:
import NextAuth, { AuthOptions } from "next-auth"; import { FirestoreAdapter } from "@next-auth/firebase-adapter"; import CredentialsProvider from "next-auth/providers/credentials"; import { adminAuth } from "@/lib/firebase-admin"; export const authOptions: AuthOptions = { providers: [ CredentialsProvider({ name: "邮箱密码登录", credentials: { email: { label: "邮箱", type: "email", placeholder: "your@email.com" }, password: { label: "密码", type: "password" }, }, async authorize(credentials) { if (!credentials?.email || !credentials?.password) return null; try { // 调用Firebase Auth REST API验证邮箱密码(Admin SDK无法直接验证密码) const response = await fetch( `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${process.env.NEXT_PUBLIC_FIREBASE_API_KEY}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: credentials.email, password: credentials.password, returnSecureToken: true, }), } ); const data = await response.json(); if (!response.ok || data.error) throw new Error(data.error?.message || "登录失败"); // 通过Admin SDK获取完整用户信息 const userRecord = await adminAuth.getUserByEmail(credentials.email); return { id: userRecord.uid, email: userRecord.email, name: userRecord.displayName, image: userRecord.photoURL, }; } catch (error) { console.error("登录验证失败:", error); return null; } }, }), ], adapter: FirestoreAdapter({ credential: admin.credential.cert({ projectId: process.env.FIREBASE_PROJECT_ID, clientEmail: process.env.FIREBASE_CLIENT_EMAIL, privateKey: process.env.FIREBASE_PRIVATE_KEY!.replace(/\\n/g, "\n"), }), }) as AuthOptions["adapter"], callbacks: { async signIn({ user }) { return !!user; // 用户验证通过则允许登录 }, async jwt({ token, user }) { // 将用户信息写入JWT if (user) { token.id = user.id; token.email = user.email; token.name = user.name; token.picture = user.image; } return token; }, async session({ session, token }) { // 将JWT中的信息同步到会话 if (session.user) { session.user.id = token.id as string; session.user.email = token.email as string; session.user.name = token.name as string; session.user.image = token.picture as string; } return session; }, }, pages: { signIn: "/login", }, session: { strategy: "jwt", }, secret: process.env.NEXTAUTH_SECRET, // 必须设置此环境变量 }; export const handler = NextAuth(authOptions); export { handler as GET, handler as POST };
步骤4:客户端登录页面示例
创建app/login/page.tsx:
"use client"; import { signIn } from "next-auth/react"; import { useState } from "react"; export default function LoginPage() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); const result = await signIn("credentials", { email, password, redirect: false, // 关闭自动跳转,手动处理结果 }); if (result?.error) { setError(result.error); } else { window.location.href = "/"; // 登录成功跳转到首页 } }; return ( <div className="max-w-md mx-auto mt-10"> <h1 className="text-2xl font-bold mb-6">登录</h1> <form onSubmit={handleSubmit} className="space-y-4"> <div> <label className="block mb-1">邮箱</label> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} className="w-full p-2 border rounded" required /> </div> <div> <label className="block mb-1">密码</label> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} className="w-full p-2 border rounded" required /> </div> {error && <p className="text-red-500">{error}</p>} <button type="submit" className="w-full p-2 bg-blue-500 text-white rounded hover:bg-blue-600" > 登录 </button> </form> </div> ); }
步骤5:配置环境变量
在.env.local中添加:
NEXTAUTH_SECRET=你的随机密钥(可通过`openssl rand -hex 32`生成) NEXT_PUBLIC_FIREBASE_API_KEY=你的Firebase项目API Key FIREBASE_PROJECT_ID=你的Firebase项目ID FIREBASE_CLIENT_EMAIL=你的Firebase服务账号邮箱 FIREBASE_PRIVATE_KEY=你的Firebase服务账号私钥(注意替换换行符为`\n`)
关键说明
- CredentialsProvider是核心:必须配置此提供者才能启用邮箱密码登录
- Firebase密码验证限制:Admin SDK无法直接验证用户密码,因此需要调用Firebase官方REST API完成验证
- 会话同步:通过
jwt和session回调确保用户信息在会话中正确传递 - 错误处理:客户端使用
redirect: false可以获取登录结果,便于展示错误信息
内容的提问来源于stack exchange,提问作者José Carlos
相关产品推荐
相关产品推荐

