agentsclimarketplace

Better auth firebase auth

Skill yultyyev/better-auth-firebase-auth

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.From its SKILL.md

Install
npx -y skills add yultyyev/better-auth-firebase-auth

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

3 things to look at

  • reads credentialsReads from 3 credential sources: `FIREBASE_PROJECT_ID` and 2 more.
  • 18 stars18 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.
  • runs commandsInstructs the agent to run 1 command, including `pnpm add better-auth-firebase-auth firebase-admin firebase better-auth`.

SKILL.md

7.1 KB, ~1.6k tokens by cl100k_base, 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-authGitHub · 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

OptionDefaultNotes
useClientSideTokenstruefalse = server handles Firebase client SDK (needs firebaseConfig)
overrideEmailPasswordFlowfalseIntercept Better Auth's /sign-in/email and /sign-up/email routes
serverSideOnlyfalseNo endpoints registered; use hooks only
sessionExpiresInDays7Better Auth session lifetime
passwordResetUrlCustom URL for password reset page
getPhoneUserFallbackEmail${uid}@firebase.localStable email for phone-only users

Runtime support

RuntimeSupported
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 firebaseAuthPlugin in client code — crashes on firebase-admin. Always use the /server path on the server and /client path 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 containersignInWithPhoneNumber requires a RecaptchaVerifier with a DOM element id. For invisible reCAPTCHA use size: "invisible".
  • Firebase Admin not initialized before getAuth() — call initializeApp() once before passing getAuth() to the plugin.
  • Using overrideEmailPasswordFlow: true without firebaseConfig — 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 to cert().

What ships with it: 33 files

105.4 KB alongside SKILL.md, 12 of them executable

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.