agentsclimarketplace

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

Install
npx -y skills add yultyyev/better-auth-firestore --skill firestore-better-auth

Assembled 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-firestoreGitHub · 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

OptionTypeDefaultDescription
firestoreFirestoregetFirestore()Firebase Admin Firestore instance
namingStrategy"default" | "snake_case""default"Collection naming convention
collectionsobjectsee belowOverride individual collection names
debugLogsbooleanfalseEnable 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

RuntimeSupported
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:

  1. First confirm the project is on the latest version — many reports (e.g. the composite-index error) are already fixed.
  2. Only file an issue when the user explicitly asks — never open one autonomously.
  3. 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

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.