Firebase Authentication plugin for Better Auth. Integrate Firebase Auth with Better Auth, allowing users to authenticate using Firebase Auth and create Better Auth sessions.
- Install:
pnpm add better-auth-firebase-auth firebase-admin firebase better-auth
Firebase Authentication provides several advantages when integrated with Better Auth:
- 🔥 Built-in Email Service - Password reset emails, email verification, and account management emails work out of the box. No need to set up SendGrid, Resend, or other email providers.
- 🌍 Global Infrastructure - Firebase Auth is backed by Google's infrastructure, ensuring high availability and low latency worldwide.
- 🔐 Battle-Tested Security - Industry-standard OAuth flows and security best practices built-in.
- 📱 Multi-Platform SDKs - Consistent authentication across web, iOS, and Android applications.
- 🎨 Customizable Email Templates - Easily customize email templates directly in the Firebase Console.
- 🚀 Quick Setup - Get authentication working in minutes without managing your own auth infrastructure.
Use Case: Perfect for applications that want robust authentication with email functionality without the complexity of setting up and maintaining email infrastructure.
npm install better-auth-firebase-auth firebase-admin firebase better-authpnpm add better-auth-firebase-auth firebase-admin firebase better-authyarn add better-auth-firebase-auth firebase-admin firebase better-authbun add better-auth-firebase-auth firebase-admin firebase better-authTo prevent bundling issues where client-side code tries to include server-side dependencies (like firebase-admin), the package provides separate export paths:
import { firebaseAuthClientPlugin } from "better-auth-firebase-auth/client";Use this import in client components, browser-only code, or anywhere the code will run in the browser. This ensures bundlers don't try to include firebase-admin.
import { firebaseAuthPlugin } from "better-auth-firebase-auth/server";Use this import in API routes, server-side code, or server components where Node.js is available.
import { firebaseAuthPlugin, firebaseAuthClientPlugin } from "better-auth-firebase-auth";The main entry point still exports both plugins for backward compatibility, but using the specific paths above is recommended to avoid bundling issues.
- ✅ Client-side and server-side token generation modes
- ✅ Optional override of Better Auth's built-in email/password flow
- ✅ Password reset functionality with email verification
- ✅ Server-side only mode (hidden endpoints, all auth through hooks)
- ✅ Sign in with Google
- ✅ Sign in with email/password
- ✅ Full TypeScript support
This plugin currently supports the following Firebase Authentication methods:
- Google Sign-In - OAuth provider (
signInWithGoogle) - Email/Password - Email-based authentication (
signInWithEmail)- Sign up with email/password
- Sign in with email/password
- Password reset flow with email verification
- Custom reset URLs
The following Firebase Auth providers are available in Firebase but not yet implemented in this plugin:
Social Providers:
- GitHub
- Twitter/X
- Microsoft
- Apple
- Yahoo
Phone Authentication:
- Phone number sign-in with SMS verification
- Multi-factor authentication (MFA)
Other Methods:
- Anonymous authentication
- Custom authentication tokens
- SAML/OpenID Connect providers
- Game Center (iOS)
- Play Games (Android)
Note: You can still use these providers directly with the Firebase Auth SDK in your application, but they won't automatically create Better Auth sessions. Contributions to add support for additional providers are welcome! See the Contributing section.
Server-side setup (API routes, lib/auth.ts):
import { betterAuth } from "better-auth";
import { firebaseAuthPlugin } from "better-auth-firebase-auth/server";
import { getAuth } from "firebase-admin/auth";
export const auth = betterAuth({
plugins: [
firebaseAuthPlugin({
useClientSideTokens: true, // Client generates Firebase tokens
firebaseAdminAuth: getAuth(), // Firebase Admin SDK instance
}),
],
});Client-side setup (React components, lib/auth-client.ts):
import { createAuthClient } from "better-auth/react";
import { firebaseAuthClientPlugin } from "better-auth-firebase-auth/client";
export const authClient = createAuthClient({
plugins: [
firebaseAuthClientPlugin({
// Optional: Add Firebase client config for additional features
}),
],
});Server-side setup (API routes, lib/auth.ts):
import { betterAuth } from "better-auth";
import { firebaseAuthPlugin } from "better-auth-firebase-auth/server";
import { getAuth } from "firebase-admin/auth";
import type { FirebaseOptions } from "firebase/app";
const firebaseConfig: FirebaseOptions = {
apiKey: process.env.FIREBASE_API_KEY!,
authDomain: process.env.FIREBASE_AUTH_DOMAIN!,
projectId: process.env.FIREBASE_PROJECT_ID!,
};
export const auth = betterAuth({
plugins: [
firebaseAuthPlugin({
useClientSideTokens: false, // Server handles Firebase Auth
firebaseAdminAuth: getAuth(),
firebaseConfig, // Required for server-side mode
}),
],
});Client-side setup (React components, lib/auth-client.ts):
import { createAuthClient } from "better-auth/react";
import { firebaseAuthClientPlugin } from "better-auth-firebase-auth/client";
export const authClient = createAuthClient({
plugins: [
firebaseAuthClientPlugin({
// No Firebase config needed - server handles everything
}),
],
});Before using password reset, configure your Firebase project:
- Go to Firebase Console
- Select your project → Authentication → Sign-in method
- Enable Email/Password provider
- Click Save
- Go to Authentication → Settings → Authorized domains
- Add your application domains:
- Development:
localhost(already included by default) - Production:
yourdomain.com,www.yourdomain.com
- Development:
⚠️ Important: Any domain used inpasswordResetUrlMUST be in this list, or Firebase will reject the request
- Go to Authentication → Templates → Password reset
- Customize the email subject, body, and sender name
- The email will contain a link to your
passwordResetUrlwith the reset code
import { betterAuth } from "better-auth";
import { firebaseAuthPlugin } from "better-auth-firebase-auth/server";
export const auth = betterAuth({
plugins: [
firebaseAuthPlugin({
firebaseConfig: {
apiKey: process.env.FIREBASE_API_KEY!,
authDomain: process.env.FIREBASE_AUTH_DOMAIN!,
projectId: process.env.FIREBASE_PROJECT_ID!,
},
passwordResetUrl: "https://myapp.com/reset-password", // Your custom reset page
// OR omit passwordResetUrl to use Firebase's default URL
}),
],
});Note: If passwordResetUrl is not provided, Firebase uses its default URL (https://YOUR_PROJECT.firebaseapp.com/__/auth/action), which works without any additional setup but doesn't match your app's branding.
await authClient.sendPasswordReset({
email: "[email protected]"
});
// User receives email with link like: https://myapp.com/reset-password?oobCode=ABC123&mode=resetPasswordOn your reset password page (/reset-password):
import { extractOobCodeFromUrl } from "better-auth-firebase-auth/client";
// Extracts oobCode from current URL query parameters
const oobCode = extractOobCodeFromUrl();
if (!oobCode) {
// Show error: Invalid or missing reset code
return;
}try {
const { valid, email } = await authClient.verifyPasswordResetCode({
oobCode
});
// Show reset form with email pre-filled
console.log("Reset password for:", email);
} catch (error) {
// Show error: Invalid or expired reset code
}try {
await authClient.confirmPasswordReset({
oobCode,
newPassword: "newSecurePassword123",
});
// Success! Redirect to login page
} catch (error) {
// Show error: Failed to reset password
}// app/reset-password/page.tsx
"use client";
import { useState, useEffect } from "react";
import { authClient } from "@/lib/auth-client";
import { extractOobCodeFromUrl } from "better-auth-firebase-auth/client";
export default function ResetPasswordPage() {
const [oobCode, setOobCode] = useState<string | null>(null);
const [email, setEmail] = useState<string>("");
const [newPassword, setNewPassword] = useState("");
const [error, setError] = useState("");
const [success, setSuccess] = useState(false);
useEffect(() => {
const code = extractOobCodeFromUrl();
if (!code) {
setError("Invalid or missing reset code");
return;
}
setOobCode(code);
// Verify the code
authClient.verifyPasswordResetCode({ oobCode: code })
.then(({ email }) => setEmail(email))
.catch(() => setError("Invalid or expired reset code"));
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!oobCode) return;
try {
await authClient.confirmPasswordReset({
oobCode,
newPassword,
});
setSuccess(true);
} catch (err) {
setError("Failed to reset password");
}
};
if (error) return <div>Error: {error}</div>;
if (success) return <div>Password reset successful! <a href="/login">Login</a></div>;
return (
<form onSubmit={handleSubmit}>
<h1>Reset Password</h1>
<p>Email: {email}</p>
<input
type="password"
placeholder="New password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
required
/>
<button type="submit">Reset Password</button>
</form>
);
}Error: "Invalid action code"
- The reset link has expired (default: 1 hour)
- The code has already been used
- Solution: Request a new password reset email
Error: "Unauthorized domain"
- Your
passwordResetUrldomain is not in Firebase's authorized domains list - Solution: Add the domain in Firebase Console → Authentication → Settings → Authorized domains
Email not received
- Check spam/junk folder
- Verify the email address is registered in Firebase Auth
- Check Firebase Console → Authentication → Templates for email settings
firebaseAuthPlugin({
useClientSideTokens?: boolean; // Default: true
overrideEmailPasswordFlow?: boolean; // Default: false
serverSideOnly?: boolean; // Default: false
firebaseAdminAuth?: admin.auth.Auth; // Optional
firebaseConfig?: FirebaseOptions; // Required for server-side mode
passwordResetUrl?: string; // Custom URL for password reset page
sessionExpiresInDays?: number; // Default: 7
});See the minimal example for a complete Next.js setup demonstrating the plugin usage.
For storing Better Auth data in Firestore, see @yultyyev/better-auth-firestore:
npm install @yultyyev/better-auth-firestoreimport { firestoreAdapter } from "@yultyyev/better-auth-firestore";
export const auth = betterAuth({
database: firestoreAdapter(),
plugins: [firebaseAuthPlugin({ ... })],
});Contributions are welcome! Please follow the Better Auth Contributing Guide for development setup and code style.
MIT