Listen to this Post

Introduction:
Phone-based authentication using services like Firebase is a cornerstone of modern application development, offering a seemingly straightforward path to user verification. However, as developers rush to implement these features, critical security and cost oversight pitfalls emerge, turning a simple OTP flow into a vector for financial loss and system compromise. This deep dive moves beyond basic implementation to expose the hardening required for a production-ready, secure, and cost-controlled auth system.
Learning Objectives:
- Implement Firebase Phone Authentication with proper security constraints and reCAPTCHA enforcement.
- Harden your Firebase project and MERN stack backend against API key leakage and credential abuse.
- Establish monitoring and alerts to prevent billing shock from the Firebase Blaze plan’s pay-as-you-go model.
- Understand the inherent security trade-offs of SMS OTP and implement supplementary safeguards.
You Should Know:
- The Firebase Setup: More Than Just Enabling a Service
While Firebase Console makes enabling Phone Authentication a checkbox, the foundational configuration dictates your security posture and cost resilience.
Step‑by‑step guide:
Step 1: Project Initialization & Plan Selection
Navigate to the Firebase Console. Create a new project or select an existing one. The critical decision point is the pricing plan. The “Spark” (free) plan has strict quotas. The “Blaze” (pay-as-you-go) plan, which the post author used, scales automatically and offers an initial credit ($300 as of this writing) but can incur charges once exceeded. Enable the Blaze plan only if necessary, and immediately set budget alerts.
Use Firebase CLI to initialize project and check billing status firebase init firebase projects:list Budget alerts must be configured in Google Cloud Console
Step 2: Enabling Phone Auth & reCAPTCHA
In your Firebase project console, navigate to “Build” > “Authentication” > “Sign-in method”. Enable “Phone”. You must configure reCAPTCHA verification. For web apps, choose “reCAPTCHA v3” (invisible) or “v2” (checkbox). Integration is mandatory, as the author noted. Failure to properly integrate reCAPTCHA client-side will block authentication attempts.
- The reCAPTCHA Conundrum: It’s Not Optional, It’s Your First Defense
The post mentions reCAPTCHA was “hard to integrate.” This is because it’s a primary defense against bot-driven phone number enumeration and SMS pumping attacks that can drain your credits.
Step‑by‑step guide:
Step 1: Get reCAPTCHA Site Keys
Register your application at the Google reCAPTCHA Admin. For Firebase Phone Auth, “reCAPTCHA v3” is recommended for a seamless UX. Note the SITE_KEY and SECRET_KEY. The secret key must be stored on your backend, never in client-side code.
Step 2: Client-Side Integration in your React App
In your frontend (App.js or auth component), load the reCAPTCHA script and initialize it with your SITE_KEY.
import { initializeApp } from "firebase/app";
import { getAuth, RecaptchaVerifier, signInWithPhoneNumber } from "firebase/auth";
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
// Create an invisible recaptcha verifier
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'sign-in-button', {
'size': 'invisible',
'callback': (response) => {
// reCAPTCHA solved, proceed with phone sign-in
}
});
// Later, to trigger sign-in
const phoneNumber = "+1234567890";
const appVerifier = window.recaptchaVerifier;
signInWithPhoneNumber(auth, phoneNumber, appVerifier)
.then((confirmationResult) => {
// SMS sent. Prompt user for OTP.
window.confirmationResult = confirmationResult;
}).catch((error) => {
console.error("SMS not sent", error);
});
- Securing Your Credentials: The Firebase API Key is Not a Secret (And How to Live With It)
The configuration object containing your `apiKey` is exposed in your frontend code. Attackers can extract it and use it to make authenticated requests to Firebase services, potentially abusing your resources.
Step‑by‑step guide:
Step 1: Understand the Threat
The `apiKey` identifies your project, but when combined with loose security rules, it can be used for abuse. You must pair it with robust security rules.
Step 2: Implement Firebase App Check (Critical)
Firebase App Check helps protect your resources by attesting that incoming requests come from your genuine app. Enforce it on Cloud Functions and Firestore.
Install via CLI firebase appcheck:apps:create --platform=web YOUR_PROJECT_ID
Step 3: Use Environment Variables & Restrict HTTP Referrers
Never hardcode config. Use environment variables in your MERN app.
In your .env file in the React root (client-side) REACT_APP_FIREBASE_API_KEY=your_api_key_here REACT_APP_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com ...
In the Firebase Console, under “Project settings” > “General” > “Your apps”, restrict your API key to specific HTTP referrers (your app’s domains) and iOS/Android app IDs.
- Hardening the Backend: Your MERN Server is the Gatekeeper
The final OTP verification happens client-side with Firebase, but your server must establish a session and validate the user’s ID token before granting access to your data.
Step‑by‑step guide:
Step 1: Verify the Firebase ID Token on Your Express Server
After the user signs in on the client, send the Firebase ID Token to your Express.js backend to verify its authenticity.
// Client-side: Get ID token after successful OTP verification
const user = auth.currentUser;
const idToken = await user.getIdToken();
// Send to your backend API endpoint (e.g., /sessionLogin)
fetch('/api/sessionLogin', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ idToken })
});
// Server-side (Express route - /api/sessionLogin)
const admin = require('firebase-admin');
// Initialize the Firebase Admin SDK with a service account key
admin.initializeApp({
credential: admin.credential.cert(serviceAccountKey)
});
app.post('/api/sessionLogin', async (req, res) => {
const { idToken } = req.body;
try {
const decodedToken = await admin.auth().verifyIdToken(idToken);
const uid = decodedToken.uid;
// Set session cookies or create a custom session for the user `uid`
req.session.uid = uid;
res.json({ status: 'success' });
} catch (error) {
res.status(401).json({ error: 'Invalid ID Token' });
}
});
Step 2: Protect All API Routes
Use a middleware to check for a valid session on all protected routes.
const isAuthenticated = (req, res, next) => {
if (req.session.uid) {
return next();
}
res.status(403).send('Unauthorized');
};
app.get('/api/protectedData', isAuthenticated, (req, res) => {
// Serve data
});
- Combating Abuse: SMS Quotas, Number Recycling, and Monitoring
The author noted Firebase blocked OTP sends to a number after a few trials. This is an anti-abuse measure. You must build your own logic to handle this gracefully and monitor for attacks.
Step‑by‑step guide:
Step 1: Implement Client-Side Attempt Counting
Track failed attempts locally and show user-friendly messages before Firebase blocks the number.
Step 2: Use Cloud Functions for Firestore to Log Attempts
Create a Firebase Cloud Function triggered by Auth events to log sign-in attempts (success/failure, phone number, IP) to a Firestore collection with strict security rules.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.logAuthenticationAttempt = functions.auth.user().onCreate((user) => {
const phone = user.phoneNumber;
const timestamp = admin.firestore.FieldValue.serverTimestamp();
return admin.firestore().collection('authLogs').add({
uid: user.uid,
phone: phone,
event: 'USER_CREATED',
source: 'FIREBASE_AUTH',
timestamp: timestamp
});
});
Step 3: Set Up Billing Alerts in Google Cloud Platform
This is non-negotiable. In the Google Cloud Console, navigate to your billing account, create a budget, and set up alerts (e.g., at 50%, 90%, 100% of your credit or a fixed amount). Configure email notifications to multiple team members.
What Undercode Say:
- The True Cost of “Free” Credits: The Blaze plan’s auto-scaling is a double-edged sword. A successful SMS pumping attack, where bots request OTPs to thousands of numbers, can burn through $300 in credits in minutes, leaving you liable for unexpected charges. Budget alerts are your last line of financial defense.
- SMS OTP is Inherently Vulnerable: This method is susceptible to SIM-swapping attacks, SS7 protocol exploits, and interception. For high-value applications, it should be combined with a second factor (2FA) or replaced with more secure methods like authenticator apps or WebAuthn passkeys where feasible.
Prediction:
The convenience of Firebase and similar BaaS (Backend-as-a-Service) platforms will continue to democratize development but will also concentrate security risk. As adoption grows, we will see a sharp rise in automated attacks targeting misconfigured Firebase instances, leading to significant data breaches and financial losses for startups. The future will demand built-in, intelligent anomaly detection at the BaaS layer itself, moving beyond simple quotas to AI-driven behavioral analysis that can distinguish legitimate user spikes from coordinated attacks. Furthermore, the industry will gradually pivot away from SMS-based OTP towards phishing-resistant FIDO2/WebAuthn standards, even for mainstream applications, rendering today’s common implementation a legacy security model.
▶️ Related Video (74% Match):
🎯Let’s Practice For Free:
IT/Security Reporter URL:
Reported By: Manupriya Dhanush – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅


