Note: If you're using
@yultyyev/better-auth-firestore, please migrate tobetter-auth-firestore. The scoped package is deprecated. See Migration from Scoped Package below.
Firestore (Firebase Admin SDK) adapter for Better Auth. A drop-in replacement for the Auth.js Firebase adapter with matching data shape.
- Install:
pnpm add better-auth-firestore firebase-admin better-auth - Docs: Quickstart • Options • Migration • Emulator
- Example: See
/examples/minimalfor a complete Next.js App Router example
For Firebase Authentication integration with Better Auth, see better-auth-firebase-auth. It provides:
- Firebase Authentication provider support (Email/Password, Google, etc.)
- Client-side or server-side token generation
- Password reset functionality
- Full TypeScript support
Use better-auth-firebase-auth for authentication and better-auth-firestore for data storage.
npm install better-auth-firestore firebase-admin better-authpnpm add better-auth-firestore firebase-admin better-authyarn add better-auth-firestore firebase-admin better-authbun add better-auth-firestore firebase-admin better-authimport { firestoreAdapter } from "better-auth-firestore";
import { createAuth } from "better-auth";
import { getFirestore } from "firebase-admin/firestore";
export const auth = createAuth({
adapter: firestoreAdapter({ firestore: getFirestore() })
});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({
// ... your Better Auth options
database: firestoreAdapter({
firestore,
namingStrategy: "default", // or "snake_case"
collections: {
// users: "users",
// sessions: "sessions",
// accounts: "accounts",
// verificationTokens: "verificationTokens",
},
}),
});- Go to Firebase Console
- Click "Add project" or "Create a project"
- Enter a project name and follow the setup wizard
- In your Firebase project, go to Build → Firestore Database
- Click "Create database"
- Choose your preferred security rules mode (you can update rules later)
- Select a location for your database
The adapter requires a composite index on the verification collection. Choose one of the following methods:
Option A: Create via Firebase Console (Recommended)
You can generate a direct link that pre-fills the index creation form:
import { generateIndexSetupUrl } from "better-auth-firestore";
// Generate the URL (pre-fills the form automatically)
const url = generateIndexSetupUrl(
process.env.FIREBASE_PROJECT_ID!,
"(default)", // or your database ID if using a named database
"verification" // or your custom collection name
);
console.log("Open this URL to create the index:", url);Or manually:
- Open:
https://console.firebase.google.com/project/YOUR_PROJECT_ID/firestore/indexes - Click "Create Index"
- Configure:
- Collection ID:
verification - Fields:
identifier(Ascending)createdAt(Descending)__name__(Descending)
- Query scope: Collection
- Collection ID:
- Click "Create" and wait for the index to build (usually a few minutes)
Option B: Use firestore.indexes.json Template
- Copy
firestore.indexes.jsonfromnode_modules/better-auth-firestore/to your project root - (Optional) Update collection name if using custom
collections.verificationTokens - Deploy:
firebase deploy --only firestore:indexes
Note: If you're using a custom collection name for verification tokens (via
collections.verificationTokens), replaceverificationwith your custom collection name in the index configuration.
- Go to Project Settings (gear icon) → Service Accounts
- Under "Firebase Admin SDK", click "Generate new private key"
- Download the JSON file (keep it secure - never commit it to version control)
From the downloaded service account JSON file, extract these values:
project_id→FIREBASE_PROJECT_IDclient_email→FIREBASE_CLIENT_EMAILprivate_key→FIREBASE_PRIVATE_KEY(requires newline replacement - see Troubleshooting)
Alternative: You can use the JSON file directly by setting GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your service account JSON file.
The adapter uses the Firebase Admin SDK (server-side), so Firestore security rules should deny direct client access. See Firestore Security Rules below.
Required environment variables:
FIREBASE_PROJECT_ID- Your Firebase project IDFIREBASE_CLIENT_EMAIL- Service account email from the JSON fileFIREBASE_PRIVATE_KEY- Service account private key (with newlines properly escaped)
Note: The FIREBASE_PRIVATE_KEY often contains literal \n characters in environment variables. See Troubleshooting for how to handle this.
firestoreAdapter({
firestore?: Firestore;
namingStrategy?: "default" | "snake_case";
collections?: { users?: string; sessions?: string; accounts?: string; verificationTokens?: string };
debugLogs?: boolean | DBAdapterDebugLogOption;
});Default collection names:
users: "users"sessions: "sessions"accounts: "accounts"verificationTokens: "verification_tokens" (snake_case) or "verificationTokens" (default)
firestoreAdapter({
firestore,
debugLogs: true, // Enable verbose logging
});| Runtime | Supported | Notes |
|---|---|---|
| Node 18+ | ✅ | Recommended |
| Next.js (App Router) | ✅ | Server routes only |
| Cloud Functions / Cloud Run | ✅ | Provide FIREBASE_* creds |
| Vercel Edge / CF Workers | ❌ | Firestore Admin SDK not supported at Edge runtime |
The adapter maintains the same data shape as Auth.js/NextAuth for seamless migration:
| Collection | Typical fields |
|---|---|
users |
id, email, name, image, createdAt, updatedAt |
accounts |
provider, providerAccountId, userId, access_token, refresh_token |
sessions |
sessionToken, userId, expires |
verificationTokens |
identifier, token, expires |
Defaults: Collections default to
users,sessions,accounts,verification_tokens(snake_case) /verificationTokens(default). See Options to customize collection names.Note: The
verificationcollection requires a composite index onidentifier(ASC),createdAt(DESC),__name__(DESC). See Firebase Setup - Step 3 for setup instructions.
Since this adapter uses the Firebase Admin SDK (server-side), Firestore security rules should deny direct client access:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}| Feature | Better Auth Firestore | Auth.js Firebase Adapter |
|---|---|---|
| Status | ✅ Active development | Now maintained by Better Auth team (announcement) |
| Firebase Admin SDK | ✅ Uses Admin SDK | ✅ Uses Admin SDK |
| Data shape compatibility | ✅ Matching shape, migration-free | - |
| Drop-in replacement | ✅ Yes | - |
This adapter is the Better Auth-native solution for Firestore users, recommended for new projects.
If you're currently using @yultyyev/better-auth-firestore, migrate to better-auth-firestore:
-
Update package name in your dependencies:
npm uninstall @yultyyev/better-auth-firestore npm install better-auth-firestore # or pnpm remove @yultyyev/better-auth-firestore pnpm add better-auth-firestore -
Update import statements:
// Before import { firestoreAdapter } from "@yultyyev/better-auth-firestore"; // After import { firestoreAdapter } from "better-auth-firestore";
That's it! The API is identical, so no code changes are needed beyond the import path.
For complete migration steps, see the Better Auth NextAuth Migration Guide, which covers route handlers, client setup, and server-side session handling.
This adapter uses the same default collection names and field names as Auth.js Firebase adapter, making it a drop-in replacement for the database adapter portion of your migration:
- Collection names:
users,sessions,accounts,verificationTokens(same as Auth.js) - Field names:
sessionToken,userId,providerAccountId, etc. (same as Auth.js) - Data shape: Identical, so no data migration scripts needed
Simply replace your Auth.js Firebase adapter with this one:
// Before (Auth.js)
import { FirestoreAdapter } from "@auth/firebase-adapter";
// After (Better Auth)
import { firestoreAdapter } from "better-auth-firestore";
// Same Firestore instance, same collections, same data shape
export const auth = betterAuth({
database: firestoreAdapter({ firestore }),
});If you were using custom collection names with Auth.js, you can override them:
firestoreAdapter({
firestore,
collections: {
accounts: "authjs_accounts", // or whatever custom names you were using
// ... other overrides
},
});firestoreAdapter({
firestore,
namingStrategy: "snake_case",
});firestoreAdapter({
firestore,
collections: {
accounts: "accounts", // or your custom collection names
// ... other overrides
},
});// app/api/auth/[...all]/route.ts
import { toNextJsHandler } from "better-auth/next-js";
import { auth } from "@/lib/auth";
export const { GET, POST } = toNextJsHandler(auth);import { firestoreAdapter } from "better-auth-firestore";
import { createAuth } from "better-auth";
import { initializeApp, cert } from "firebase-admin/app";
import { getFirestore } from "firebase-admin/firestore";
const app = 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 = createAuth({
adapter: firestoreAdapter({ firestore: getFirestore(app) }),
});# start emulator (example)
docker run -d --rm \
--name auth-firestore \
-p 8080:8080 \
google/cloud-sdk:emulators gcloud beta emulators firestore start \
--host-port=0.0.0.0:8080
export FIRESTORE_EMULATOR_HOST=localhost:8080
pnpm vitest runSymptom: Authentication fails or you see errors about invalid private key format.
Fix: Environment variables often store newlines as literal \n strings. Replace them at runtime:
privateKey: process.env.FIREBASE_PRIVATE_KEY!.replace(/\\n/g, "\n")Symptom: Firebase Admin SDK requests hang or time out during local development.
Fix: Use the Firestore Emulator and set FIRESTORE_EMULATOR_HOST=localhost:8080 before running your app. See Testing with Firestore Emulator for setup instructions.
Symptom: Queries on verification tokens fail with errors about missing index or insufficient permissions.
Fix: Create the required composite index on the verification collection. See Firebase Setup - Step 3 for detailed instructions.
You can generate a direct link using:
import { generateIndexSetupUrl } from "better-auth-firestore";
const url = generateIndexSetupUrl(process.env.FIREBASE_PROJECT_ID!);
console.log(url); // Open this URL to create the index- Better Auth Documentation
- Better Auth Adapter Guide
- Auth.js Firebase Adapter (legacy, for reference)
- Auth.js joins Better Auth - Announcement
pnpm buildMIT.