Firestore better auth
Skill yultyyev/better-auth-firestore/skills/firestore-better-auth
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.From its SKILL.md
npx -y skills add yultyyev/better-auth-firestore --skill firestore-better-authAssembled 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.
- 9 stars9 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 4 commands, including `pnpm add better-auth-firestore firebase-admin better-auth` and 3 more.
SKILL.md
7.2 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
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", // or "snake_case"
collections: {
// users: "users",
// sessions: "sessions",
// accounts: "accounts",
// verificationTokens: "verificationTokens",
},
}),
});
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.
// Before (Auth.js)
import { FirestoreAdapter } from "@auth/firebase-adapter";
// After (Better Auth)
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)
# Start emulator
docker run -d --rm -p 8080:8080 google/cloud-sdk:emulators \
gcloud beta emulators firestore start --host-port=0.0.0.0:8080
# Set env and start dev server
FIRESTORE_EMULATOR_HOST=localhost:8080 pnpm dev
# Run tests
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 indexon verification tokens — You're on a version older than v1.1. Upgradebetter-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 tocert(). - 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-firestorepackage 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 anycreate_compositeindex URL (it encodes the project path). Include the package version and a minimal repro instead.
Issues: https://github.com/yultyyev/better-auth-firestore/issues
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.