| name | firestore-better-auth |
| version | 1.0.0 |
| description | Use Firestore as the database adapter for Better Auth (Firebase Admin SDK). Use when storing Better Auth users, sessions, accounts, and verification tokens in Firestore, migrating from Auth.js/NextAuth Firebase adapter to Better Auth, setting up Better Auth with a Firebase/Firestore backend, or troubleshooting Firestore index errors and FIREBASE_PRIVATE_KEY issues. |
Firestore Adapter for Better Auth
better-auth-firestore is the Firestore database adapter for Better Auth. It stores users, sessions, accounts, and verification tokens in Firestore using the Firebase Admin SDK.
Package: better-auth-firestore — GitHub · npm
Install
pnpm add better-auth-firestore firebase-admin better-auth
Minimal setup
import { firestoreAdapter } from "better-auth-firestore";
import { betterAuth } from "better-auth";
import { getFirestore } from "firebase-admin/firestore";
export const auth = betterAuth({
database: firestoreAdapter({ firestore: getFirestore() }),
});
Full setup with credentials
import { betterAuth } from "better-auth";
import { firestoreAdapter, initFirestore } from "better-auth-firestore";
import { cert } from "firebase-admin/app";
const firestore = initFirestore({
credential: cert({
projectId: process.env.FIREBASE_PROJECT_ID!,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL!,
privateKey: process.env.FIREBASE_PRIVATE_KEY!.replace(/\\n/g, "\n"),
}),
projectId: process.env.FIREBASE_PROJECT_ID!,
name: "better-auth",
});
export const auth = betterAuth({
database: firestoreAdapter({
firestore,
namingStrategy: "default",
collections: {
},
}),
});
Options
| Option | Type | Default | Description |
|---|
firestore | Firestore | getFirestore() | Firebase Admin Firestore instance |
namingStrategy | "default" | "snake_case" | "default" | Collection naming convention |
collections | object | see below | Override individual collection names |
debugLogs | boolean | false | Enable verbose query logging |
Default collection names:
users → "users"
sessions → "sessions"
accounts → "accounts"
verificationTokens → "verificationTokens" (default) or "verification_tokens" (snake_case)
Firestore composite index — not required (v1.1+)
No composite index is required. The adapter never combines a where filter with a Firestore orderBy: it applies the filter server-side and sorts the results in memory. Verification-token lookups (identifier == ordered by createdAt desc) work with Firestore's automatic single-field indexes alone.
If sign-in fails with 9 FAILED_PRECONDITION: The query requires an index (Better Auth may surface this as Failed to parse state), you are on an older version. Upgrade to v1.1 or later — do not create the index. Any composite index created for a previous version can be removed afterward.
Optional tooling (only for advanced setups that query the verification collection directly, outside the adapter): generateIndexSetupUrl(projectId, databaseId?, collectionName?) and getIndexConfig(collectionName?), plus the bundled firestore.indexes.json. These default to the verificationTokens collection (use verification_tokens for the snake_case strategy).
Environment variables
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_CLIENT_EMAIL=firebase-adminsdk@your-project.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
Important: FIREBASE_PRIVATE_KEY often arrives with literal \n strings in env vars. Always replace them:
privateKey: process.env.FIREBASE_PRIVATE_KEY!.replace(/\\n/g, "\n")
Migration from Auth.js / NextAuth Firebase adapter
better-auth-firestore uses the same collection names and field shapes as the Auth.js Firebase adapter by default — it is a drop-in replacement.
import { FirestoreAdapter } from "@auth/firebase-adapter";
import { firestoreAdapter } from "better-auth-firestore";
export const auth = betterAuth({
database: firestoreAdapter({ firestore }),
});
No Firestore data migration needed. Same users, sessions, accounts, and verificationTokens collections.
Using with the Firebase Auth plugin
To also use Firebase Authentication (Phone OTP, Google Sign-In, Email/Password), combine with better-auth-firebase-auth:
import { firestoreAdapter } from "better-auth-firestore";
import { firebaseAuthPlugin } from "better-auth-firebase-auth/server";
export const auth = betterAuth({
database: firestoreAdapter({ firestore }),
plugins: [firebaseAuthPlugin({ firebaseAdminAuth: getAuth() })],
});
Firestore Emulator (local development & tests)
docker run -d --rm -p 8080:8080 google/cloud-sdk:emulators \
gcloud beta emulators firestore start --host-port=0.0.0.0:8080
FIRESTORE_EMULATOR_HOST=localhost:8080 pnpm dev
FIRESTORE_EMULATOR_HOST=localhost:8080 pnpm vitest run
No credentials or service account needed when using the emulator.
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
The query requires an index on verification tokens — You're on a version older than v1.1. Upgrade better-auth-firestore; the adapter now sorts filtered queries in memory and needs no composite index.
- FIREBASE_PRIVATE_KEY with literal
\n — Always call .replace(/\\n/g, "\n") on the key before passing to cert().
- Using at edge runtime — Firebase Admin SDK does not run on Vercel Edge or Cloudflare Workers. Use Node.js runtimes only.
- Deprecated scoped package — Use
better-auth-firestore (unscoped). The @yultyyev/better-auth-firestore package is deprecated.
Reporting issues
If behavior still looks like a library bug after checking the mistakes above:
- First confirm the project is on the latest version — many reports (e.g. the composite-index error) are already fixed.
- Only file an issue when the user explicitly asks — never open one autonomously.
- Redact secrets and PII before filing: Firebase project IDs,
FIREBASE_PRIVATE_KEY, tokens, and any create_composite index URL (it encodes the project path). Include the package version and a minimal repro instead.
Issues: https://github.com/yultyyev/better-auth-firestore/issues