
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
better-auth-firestore
Advanced tools
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.
pnpm add better-auth-firestore firebase-admin better-auth/examples/minimal for a complete Next.js App Router examplenpx skills add yultyyev/better-auth-firestore • llms.txtFor Firebase Authentication integration with Better Auth, see better-auth-firebase-auth. It provides:
Use better-auth-firebase-auth for authentication and better-auth-firestore for data storage. They are designed to be used together:
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() })],
});
npm install better-auth-firestore firebase-admin better-auth
pnpm add better-auth-firestore firebase-admin better-auth
yarn add better-auth-firestore firebase-admin better-auth
bun add better-auth-firestore firebase-admin better-auth
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() })
});
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",
},
}),
});
No composite index is required. As of v1.1, the adapter sorts filtered queries — including verification-token lookups — in memory, so Firestore's automatic single-field indexes are sufficient. You can skip straight to the next step.
If you're upgrading from an earlier version that required a composite index on the verification collection (identifier ASC, createdAt DESC), you can safely leave that index in place or delete it — the adapter no longer depends on it.
The generateIndexSetupUrl / getIndexConfig helpers and the bundled firestore.indexes.json are still exported for advanced setups (for example, if you run your own where + orderBy queries directly against the verification collection outside the adapter). They default to the verificationTokens collection; pass "verification_tokens" when using the snake_case naming strategy, or your custom collection name.
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
});
| Better Auth | Status | Notes |
|---|---|---|
^1.5.0 | ✅ Recommended | Uses the latest API and security fixes. |
^1.4.18 | ✅ Supported | Backward-compatible for existing projects. |
For older projects: if your app still uses older Better Auth patterns (
createAuth+adapter), this adapter remains compatible, but new projects should usebetterAuth+database.
| Runtime | Supported | Notes |
|---|---|---|
| Node 18+ | ✅ | Recommended |
| Next.js on Vercel (Node.js runtime) | ✅ | Default serverless runtime — fully supported |
| Cloud Functions / Cloud Run | ✅ | Provide FIREBASE_* creds |
Vercel Edge Runtime (runtime = 'edge') | ❌ | Firebase Admin SDK requires Node.js |
| Cloudflare Workers | ❌ | Firebase Admin SDK requires Node.js |
Vercel works. The ❌ above applies only if you explicitly set
export const runtime = 'edge'on a route. The default Node.js serverless runtime on Vercel is fully supported.
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: No composite index is required. Verification-token lookups are sorted in memory. See Firebase Setup - Step 3 for details.
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:
users, sessions, accounts, verificationTokens (same as Auth.js)sessionToken, userId, providerAccountId, etc. (same as Auth.js)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 { betterAuth } 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 = betterAuth({
database: firestoreAdapter({ firestore: getFirestore(app) }),
});
The adapter fully supports the Firestore Emulator for both local development and testing. When FIRESTORE_EMULATOR_HOST is set, the Firebase Admin SDK automatically routes all requests to the emulator instead of production Firestore. Collection names remain unchanged — the adapter uses the same collections in emulator mode as in production.
# 1. Start the emulator
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
# 2. Set the env var and start your app
export FIRESTORE_EMULATOR_HOST=localhost:8080
pnpm run dev
Or add FIRESTORE_EMULATOR_HOST=localhost:8080 to your .env file (supported by Next.js, Vite, etc.).
Note: No credential or service account setup is needed when using the emulator — the Admin SDK skips authentication automatically.
export FIRESTORE_EMULATOR_HOST=localhost:8080
pnpm vitest run
FIREBASE_PRIVATE_KEY has literal \nSymptom: 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")
See also the AI Assistant Skill — agents use it to avoid this mistake during setup.
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 Using the Firestore Emulator for setup instructions. The AI Assistant Skill includes emulator commands for agents.
9 FAILED_PRECONDITION: The query requires an indexSymptom: Queries on verification tokens fail with a FAILED_PRECONDITION / "The query requires an index" error (often surfaced by Better Auth as Failed to parse state).
Fix: Upgrade to better-auth-firestore v1.1 or later. Older versions issued a where + orderBy query that required a composite index; the adapter now sorts filtered queries in memory, so no index is needed. After upgrading, the error disappears and any previously created composite index can be removed.
If you cannot upgrade immediately, create the index from the URL in the error message, or generate it with:
import { generateIndexSetupUrl } from "better-auth-firestore";
const url = generateIndexSetupUrl(process.env.FIREBASE_PROJECT_ID!);
console.log(url); // Open this URL to create the index
Yes. better-auth-firestore is designed as a drop-in replacement for the Auth.js Firebase adapter with matching collection names and field shapes by default, so most projects do not need a Firestore data migration. See Migration from Auth.js/NextAuth for the adapter-specific details. The AI Assistant Skill includes a migration guide for Cursor, Claude Code, and other agents.
better-auth-firestore and better-auth-firebase-auth?better-auth-firestore is a database adapter for storing Better Auth users, sessions, accounts, and verification tokens in Firestore through the Firebase Admin SDK. better-auth-firebase-auth is for Firebase Authentication provider integration such as Email/Password, Google sign-in, client/server token generation, and password reset flows. Use the Firestore adapter for data storage and the Firebase Auth plugin when you need Firebase Authentication features. Both packages have AI Assistant Skills on skills.sh.
This package supports any server-side Node.js runtime: Next.js on Vercel (the default serverless runtime), Cloud Functions, Cloud Run, and standalone Node.js. The only restriction is the Edge Runtime — if you explicitly set export const runtime = 'edge' on a route, the Firebase Admin SDK will not load. Standard Vercel deployments are fully supported. See Runtime compatibility for the full matrix. Agents should follow the runtime table in the AI Assistant Skill.
No. Better Auth's verification-token lookup filters by identifier and orders by createdAt, which historically required a composite index. As of v1.1 the adapter applies the filter server-side and sorts the (small, per-identifier) result set in memory, so no composite index is required. See Firestore Index (Optional) for the optional tooling that remains available.
The agent skill lives at skills/firestore-better-auth/SKILL.md. It works with Cursor, Claude Code, Codex, Copilot, Windsurf, and 70+ other agents via the skills.sh ecosystem.
The skill teaches AI assistants the correct setup, environment variable handling, and common gotchas. It also triggers when you ask about using Firestore with Better Auth, migrating from Auth.js/NextAuth, or troubleshooting FIREBASE_PRIVATE_KEY issues.
npx skills add yultyyev/better-auth-firestore
For LLM crawlers and AI search, see also llms.txt at the repo root — a curated index of documentation, the skill file, and key setup facts.
Install works today from GitHub. The skills.sh listing page and README badge appear once indexed — tracking vercel-labs/skills#1601.
pnpm build
Found a bug? First make sure you're on the latest version, then open an issue with the package version and a minimal repro. Please redact secrets and PII (Firebase project IDs, FIREBASE_PRIVATE_KEY, tokens, and create_composite index URLs).
Contributions are welcome. See CONTRIBUTING.md for development setup and code style.
MIT.
FAQs
Firestore adapter for Better Auth (Firebase Admin SDK)
We found that better-auth-firestore demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.