Better auth firebase auth
Use Firebase Authentication (Phone SMS OTP, Google, Email) with Better Auth sessions, organizations, and plugins. No Twilio needed for phone auth.
npx -y skills add yultyyev/better-auth-firebase-authAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 17 stars17 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Add Firebase Authentication (Phone SMS OTP, Google Sign-In, Email/Password) to a Better Auth app using the better-auth-firebase-auth plugin. Use when adding phone authentication to Better Auth without Twilio, integrating Firebase Auth with Better Auth sessions, working with the better-auth-firebase-auth package, or deciding between Firebase Phone Auth and Better Auth's built-in phoneNumber plugin.
SKILL.md
7.1 KB, as published. Nobody here has run it
Firebase Auth + Better Auth
better-auth-firebase-auth bridges Firebase Authentication identity providers into Better Auth sessions. Firebase verifies the user; Better Auth owns the session, users, and plugins.
Package: better-auth-firebase-auth — GitHub · npm
Decision: Firebase Phone Auth vs Better Auth phoneNumber plugin
Use better-auth-firebase-auth when:
- You want phone auth without setting up Twilio or any SMS provider
- You are already using Firebase in your project
- You want Google to manage SMS delivery, reCAPTCHA, and fraud prevention
Use Better Auth's built-in phoneNumber plugin when:
- You want no Firebase dependency
- You need a specific SMS provider for compliance or cost reasons
Install
pnpm add better-auth-firebase-auth firebase-admin firebase better-auth
Import paths — CRITICAL
Always split server and client imports. Never import firebase-admin into client bundles.
// Server ONLY (API routes, server components, server actions)
import { firebaseAuthPlugin } from "better-auth-firebase-auth/server";
// Client ONLY (React components, browser code)
import { firebaseAuthClientPlugin } from "better-auth-firebase-auth/client";
Server setup (lib/auth.ts)
import { betterAuth } from "better-auth";
import { firebaseAuthPlugin } from "better-auth-firebase-auth/server";
import { cert, getApps, initializeApp } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";
// Initialize Firebase Admin once
if (getApps().length === 0) {
initializeApp({
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 auth = betterAuth({
plugins: [
firebaseAuthPlugin({
useClientSideTokens: true, // client gets Firebase token, server only verifies
firebaseAdminAuth: getAuth(),
}),
],
});
Client setup (lib/auth-client.ts)
import { createAuthClient } from "better-auth/react";
import { firebaseAuthClientPlugin } from "better-auth-firebase-auth/client";
export const authClient = createAuthClient({
plugins: [firebaseAuthClientPlugin()],
});
Phone Authentication (SMS OTP)
Firebase sends the SMS and verifies the OTP. No Twilio needed.
Prerequisite: Enable Phone in Firebase Console → Authentication → Sign-in method.
import { getAuth, RecaptchaVerifier, signInWithPhoneNumber } from "firebase/auth";
import { authClient } from "@/lib/auth-client";
const firebaseAuth = getAuth();
// 1. Send OTP
const verifier = new RecaptchaVerifier(firebaseAuth, "recaptcha-container", {
size: "invisible",
});
const confirmation = await signInWithPhoneNumber(firebaseAuth, "+15555550100", verifier);
// 2. Confirm OTP → get Firebase token → create Better Auth session
const result = await confirmation.confirm(userEnteredCode);
const idToken = await result.user.getIdToken();
await authClient.signInWithPhone({ idToken });
Phone-only users (no email on their Firebase account) get a stable synthetic email: ${uid}@firebase.local by default. Override with getPhoneUserFallbackEmail.
Google Sign-In
import { getAuth, GoogleAuthProvider, signInWithPopup } from "firebase/auth";
const result = await signInWithPopup(getAuth(), new GoogleAuthProvider());
const idToken = await result.user.getIdToken();
await authClient.signInWithGoogle({ idToken });
Email/Password
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
const credential = await signInWithEmailAndPassword(getAuth(), email, password);
const idToken = await credential.user.getIdToken();
await authClient.signInWithEmail({ idToken });
Password reset is handled by Firebase — no email provider (SendGrid, Resend) needed:
await authClient.sendPasswordReset({ email });
Using with the Firestore adapter
To store Better Auth data in Firestore, combine with better-auth-firestore:
import { firestoreAdapter } from "better-auth-firestore";
import { firebaseAuthPlugin } from "better-auth-firebase-auth/server";
import { getAuth } from "firebase-admin/auth";
import { getFirestore } from "firebase-admin/firestore";
export const auth = betterAuth({
database: firestoreAdapter({ firestore: getFirestore() }),
plugins: [firebaseAuthPlugin({ firebaseAdminAuth: getAuth() })],
});
Remember to create the required Firestore composite index on the verification collection — see better-auth-firestore.
Key options
| Option | Default | Notes |
|---|---|---|
useClientSideTokens | true | false = server handles Firebase client SDK (needs firebaseConfig) |
overrideEmailPasswordFlow | false | Intercept Better Auth's /sign-in/email and /sign-up/email routes |
serverSideOnly | false | No endpoints registered; use hooks only |
sessionExpiresInDays | 7 | Better Auth session lifetime |
passwordResetUrl | — | Custom URL for password reset page |
getPhoneUserFallbackEmail | ${uid}@firebase.local | Stable email for phone-only users |
Runtime support
| Runtime | Supported |
|---|---|
| Node 18+ | ✅ |
| Next.js on Vercel (Node.js runtime) | ✅ Recommended |
| Cloud Functions / Cloud Run | ✅ |
Vercel Edge Runtime (runtime = 'edge') | ❌ Admin SDK requires Node.js |
| Cloudflare Workers | ❌ Admin SDK requires Node.js |
Note: Vercel deploys work fine — the restriction is only when you explicitly opt into the Edge Runtime (export const runtime = 'edge'). The default Node.js serverless runtime on Vercel is fully supported.
Common mistakes
- Importing
firebaseAuthPluginin client code — crashes onfirebase-admin. Always use the/serverpath on the server and/clientpath in the browser. - Forgetting to enable the provider in Firebase Console — Phone, Google, and Email/Password must each be explicitly enabled under Authentication → Sign-in method.
- Missing reCAPTCHA container —
signInWithPhoneNumberrequires aRecaptchaVerifierwith a DOM element id. For invisible reCAPTCHA usesize: "invisible". - Firebase Admin not initialized before
getAuth()— callinitializeApp()once before passinggetAuth()to the plugin. - Using
overrideEmailPasswordFlow: truewithoutfirebaseConfig— throws at startup. This mode requires the Firebase client SDK config. - FIREBASE_PRIVATE_KEY with literal
\n— Always call.replace(/\\n/g, "\n")on the key before passing tocert().