Firestore adapter for Better Auth (Firebase Admin SDK)
npm install better-auth-firestore


> Note: If you're using @yultyyev/better-auth-firestore, please migrate to better-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/minimal for 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.
---
``bash`
npm install better-auth-firestore firebase-admin better-auth
`bash`
pnpm add better-auth-firestore firebase-admin better-auth
`bash`
yarn add better-auth-firestore firebase-admin better-auth
`bash`
bun add better-auth-firestore firebase-admin better-auth
`ts
import { firestoreAdapter } from "better-auth-firestore";
import { createAuth } from "better-auth";
import { getFirestore } from "firebase-admin/firestore";
export const auth = createAuth({
adapter: firestoreAdapter({ firestore: getFirestore() })
});
`
`ts
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",
},
}),
});
`
1. Go to Firebase Console
2. Click "Add project" or "Create a project"
3. Enter a project name and follow the setup wizard
1. In your Firebase project, go to Build → Firestore Database
2. Click "Create database"
3. Choose your preferred security rules mode (you can update rules later)
4. 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:
`ts
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:
1. Open: https://console.firebase.google.com/project/YOUR_PROJECT_ID/firestore/indexesverification
2. Click "Create Index"
3. Configure:
- Collection ID: identifier
- Fields:
- (Ascending)createdAt
- (Descending)__name__
- (Descending)
- Query scope: Collection
4. Click "Create" and wait for the index to build (usually a few minutes)
Option B: Use firestore.indexes.json Template
1. Copy firestore.indexes.json from node_modules/better-auth-firestore/ to your project rootcollections.verificationTokens
2. (Optional) Update collection name if using custom firebase deploy --only firestore:indexes
3. Deploy:
> Note: If you're using a custom collection name for verification tokens (via collections.verificationTokens), replace verification with your custom collection name in the index configuration.
1. Go to Project Settings (gear icon) → Service Accounts
2. Under "Firebase Admin SDK", click "Generate new private key"
3. 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.
`ts`
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)
`ts`
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.verification
>
> Note: The collection requires a composite index on identifier (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:
`javascript`
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:
1. Update package name in your dependencies:
`bash`
npm uninstall @yultyyev/better-auth-firestore
npm install better-auth-firestore
# or
pnpm remove @yultyyev/better-auth-firestore
pnpm add better-auth-firestore
2. Update import statements:
`ts`
// 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)sessionToken
- Field names: , 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:
`ts
// 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:
`ts`
firestoreAdapter({
firestore,
collections: {
accounts: "authjs_accounts", // or whatever custom names you were using
// ... other overrides
},
});
`ts`
firestoreAdapter({
firestore,
namingStrategy: "snake_case",
});
`ts`
firestoreAdapter({
firestore,
collections: {
accounts: "accounts", // or your custom collection names
// ... other overrides
},
});
`ts
// app/api/auth/[...all]/route.ts
import { toNextJsHandler } from "better-auth/next-js";
import { auth } from "@/lib/auth";
export const { GET, POST } = toNextJsHandler(auth);
`
`ts
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) }),
});
`
`bashstart 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 run
`
Symptom: 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:
`ts`
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:
`ts`
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
`bash``
pnpm build
MIT.