Firebase in 2026: Auth, Firestore, and Cloud Functions
A hands-on walkthrough of Firebase in 2026: setting up Auth, modeling and querying Firestore, writing bulletproof Security Rules, and building Cloud Functions (2nd gen) with secrets and deployment.
Published on • August 12, 2026
AI Assistant

Introduction
Firebase in 2026 is a production platform, not a prototyping BaaS. Firestore ships Standard and Enterprise editions, Cloud Functions (2nd gen) runs on Eventarc, App Hosting deploys full-stack apps, and Data Connect evolved into SQL Connect for PostgreSQL. The fundamentals still hold: you assemble the plumbing yourself — authentication, a NoSQL data model, server-enforced rules, and event-driven server code. This post walks through exactly that stack with working code and one complete app flow, using the Firebase JS SDK 12.x modular API, the Firebase CLI, Security Rules, and firebase-functions/v2.
Prerequisites
- Node.js 20+ and
npm, plus a Google account for the Firebase project - Basic TypeScript/JavaScript and async/await familiarity
npm install -g firebase-tools && firebase login
1. Project setup with the Firebase CLI
firebase init functions && firebase init firestore
This writes firebase.json pointing at your rules and function source ({ "firestore": { "rules": "firestore.rules" }, "functions": { "source": "functions" } }). You only need the Blaze (pay-as-you-go) plan for Cloud Functions. Auth is free for the first 50,000 monthly active users with standard providers, and Firestore includes a daily free quota of 50,000 reads, 20,000 writes, and 1 GiB of storage.
2. Authentication: email/password, Google, and token verification
// src/firebase.js
import { initializeApp } from 'firebase/app';
import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
export const app = initializeApp({ apiKey: 'YOUR_API_KEY', authDomain: 'your-project.firebaseapp.com', projectId: 'your-project' });
export const auth = getAuth(app);
export const db = getFirestore(app);
const register = (email, password) => createUserWithEmailAndPassword(auth, email, password);
const login = (email, password) => signInWithEmailAndPassword(auth, email, password);
const loginWithGoogle = () => signInWithPopup(auth, new GoogleAuthProvider());
The habit that matters: never trust the client. An ID token is a JWT clients can forge, so verify it server-side with the Admin SDK:
// functions/src/index.ts
import { initializeApp } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';
initializeApp();
const verifyIdToken = async (idToken: string) => (await getAuth().verifyIdToken(idToken)).uid; // throws if invalid/expired
For callable functions, context.auth is already verified; for HTTPS endpoints, request the token yourself and verify it exactly as above.
3. Firestore: data modeling and queries
Firestore has no joins, so design around read paths: denormalize frequently-read fields (like authorName on a post) and use subcollections for anything that grows indefinitely. Documents cap at 1 MiB. A practical model: users/{userId} holds profile fields, posts/{postId} holds title, body, authorUid, authorName, createdAt, featured, and comments live in the unbounded subcollection posts/{postId}/comments/{commentId}. Client queries:
import { collection, addDoc, query, where, orderBy, limit, onSnapshot, Timestamp } from 'firebase/firestore';
const createPost = async (title, body) => {
const author = auth.currentUser;
await addDoc(collection(db, 'posts'), { title, body, authorUid: author.uid, authorName: author.displayName, createdAt: Timestamp.now(), featured: false });
};
const postsByUser = (userId) => query(collection(db, 'posts'), where('authorUid', '==', userId), orderBy('createdAt', 'desc'), limit(20));
const unsubscribe = onSnapshot(postsByUser('SOME_UID'), (snap) => snap.forEach((d) => console.log(d.id, d.data())));
On the free tier this stays inside the 50,000 reads/day quota; on the Enterprise edition you are billed per 4 KiB read/write units plus index writes, so keep documents small.
4. Security Rules: your real authorization layer
Clients talk to Firestore directly, so Security Rules are the only gate between a malicious user and your data. Deny by default, then open exactly what each role needs. auth is null for anonymous requests; read splits into get/list, write into create/update/delete; get()/exists() inside rules cost Firestore reads. Use rules_version = '2' and test locally with firebase emulators:start --only firestore:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function signedIn() { return request.auth != null; }
function isAuthor(uid) { return signedIn() && request.auth.uid == uid; }
match /users/{userId} { allow read, write: if isAuthor(userId); }
match /posts/{postId} {
allow read: if true;
allow create: if signedIn() && request.resource.data.authorUid == request.auth.uid;
allow update: if isAuthor(resource.data.authorUid) && request.resource.data.authorUid == resource.data.authorUid;
allow delete: if isAuthor(resource.data.authorUid);
match /comments/{commentId} {
allow read: if signedIn();
allow create: if signedIn() && request.resource.data.authorUid == request.auth.uid && request.resource.data.text.size() < 500;
allow delete: if isAuthor(resource.data.authorUid);
}
}
}
}
5. Cloud Functions (2nd gen): events, HTTPS, config, secrets
2nd gen functions run on Cloud Run and Eventarc with per-function memory/concurrency/region settings, and imports move to firebase-functions/v2. A Firestore trigger reacting to a new post, using the event’s auth context:
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
import { getFirestore } from 'firebase-admin/firestore';
import { getAuth } from 'firebase-admin/auth';
import { initializeApp } from 'firebase-admin/app';
initializeApp();
const db = getFirestore();
export const onPostCreated = onDocumentCreated('posts/{postId}', async (event) => {
const post = event.data?.data();
if (!post) return;
const uid = event.auth?.uid ?? post.authorUid; // triggering user from the event
await db.doc(`audit/${event.params.postId}`).set({ createdBy: uid, authorName: post.authorName, createdAt: new Date() });
});
An HTTPS endpoint that verifies the ID token, and a typed-parameter + Secret Manager config (no more .runtimeconfig.json):
import { onRequest } from 'firebase-functions/v2/https';
import { defineString, defineSecret } from 'firebase-functions/params';
export const whoami = onRequest({ cors: true }, async (req, res) => {
const idToken = req.headers.authorization?.replace(/^Bearer /, '');
if (!idToken) return res.status(401).json({ error: 'missing token' });
res.json({ uid: await verifyIdToken(idToken) });
});
const region = defineString('REGION', { default: 'asia-southeast1' });
const mailgunKey = defineSecret('MAILGUN_API_KEY');
export const sendWelcome = onRequest({ region: region.value(), secrets: [mailgunKey] }, async (_req, res) => {
res.json({ ok: true, keyReady: Boolean(mailgunKey.value()) });
});
Set the secret, then deploy:
firebase functions:secrets:set MAILGUN_API_KEY
firebase deploy --only functions
6. Deploying the stack
firebase deploy --only firestore:rules && firebase deploy --all
Keep firestore.rules under version control. For frontends, App Hosting deploys Next.js/Angular from a git branch and injects FIREBASE_CONFIG automatically; classic Hosting + CDN still works for static sites.
Putting It All Together
The full flow: (1) the user signs up with email/password (section 2); (2) the client writes /posts/{postId} — rules check authorUid == request.auth.uid, so a forged document is impossible (section 4); (3) Firestore triggers onPostCreated (section 5), which reads the event’s auth context and writes an audit entry with the Admin SDK, which bypasses rules because it is trusted server code; (4) the client’s onSnapshot listener renders the change in real time:
const onPublish = async () => {
await login(email, password);
await createPost('Hello Firebase', 'Rules + Functions = a secure backend');
};
That flow shows the three boundary concepts that keep a Firebase app safe: verified auth on the client, rules as the authorization gate, and Admin SDK access reserved for trusted server code.
Conclusion & Next Steps
Firebase in 2026 composes a managed identity service, a scalable NoSQL database, and a serverless event platform into a production backend. The discipline that separates good apps from leaks is boundary hygiene: verify ID tokens server-side, write least-privilege rules, keep secrets in Secret Manager, and use 2nd gen triggers for Firestore events. Next: enable the emulator suite in CI, try Enterprise edition Pipeline operations for heavy aggregation, and evaluate SQL Connect if you outgrow Firestore’s query model.
References / Sources
- https://firebase.google.com/docs/auth/web/start
- https://firebase.google.com/docs/auth/admin/verify-id-tokens
- https://firebase.google.com/docs/firestore/pricing
- https://firebase.google.com/docs/firestore/manage-data/structure-data
- https://firebase.google.com/docs/firestore/security/insecure-rules
- https://firebase.google.com/docs/firestore/extend-with-functions-2nd-gen
- https://firebase.google.com/docs/functions/config-env
- https://firebase.google.com/docs/app-hosting
- https://firebase.google.com/docs/sql-connect
- https://firebase.google.com/docs/web/modular-upgrade