
Your biggest enterprise prospect just asked whether your SaaS app supports mandatory multi-factor authentication for their team. It doesn't. You've been meaning to add it for six months. It keeps getting pushed for product features. Now it's a deal requirement. MFA implementation gets treated as a simple feature — add a TOTP field, verify the code, done. But in a B2B SaaS with organisations, team members, admin roles, and account recovery flows, the full MFA system is actually a week-plus engineering project with several non-obvious decisions that affect both security and user experience. This guide covers the complete implementation: TOTP setup, SMS fallback (and why you should be careful with it), recovery codes, enforcement policies for enterprise customers, and what to do when a user gets locked out.
💡 TL;DR
A complete MFA implementation for a B2B SaaS app covers: TOTP authentication (Google Authenticator, Authy), recovery codes (generated at setup, single-use, hashed in storage), optional SMS fallback (with caveats), org-level MFA enforcement for enterprise plans, and a lockout recovery process that doesn't create a bypass for MFA itself. The setup flow takes 3–5 days of engineering. The enforcement and recovery edge cases take another 2–3 days. Don't underestimate the recovery flow — it's where most implementations have security gaps.
Which MFA Method to Support — and in What Order
Not all MFA methods are equal. SMS-based MFA is better than nothing — but it's the weakest option on this list and has been successfully bypassed through SIM-swapping attacks. TOTP authenticator apps are the standard for B2B SaaS. Passkeys are the direction things are moving in 2026. Here's how to think about which to implement first.
MFA Method | Security Level | Implementation Effort | User Adoption |
|---|---|---|---|
TOTP (Authenticator app) | High | 3–4 days | Good — familiar to most technical users |
SMS OTP | Medium — SIM swap risk | 1–2 days (via Twilio/AWS SNS) | Excellent — lowest friction |
Email OTP | Medium — email compromise risk | 1 day | Good — no app required |
Hardware key (FIDO2/WebAuthn) | Very high | 5–7 days | Low — requires physical hardware |
Passkeys (WebAuthn) | Very high | 4–6 days | Growing — device-based biometric |
For most B2B SaaS products in 2026, start with TOTP and add SMS as a fallback. TOTP satisfies enterprise security requirements. SMS increases adoption among less technical users. Build FIDO2/WebAuthn support when you have enterprise customers who specifically request it — it's the right call for high-security environments, but it adds significant implementation complexity that most early-stage teams don't need immediately.
Implementing TOTP — The Full Setup Flow
TOTP (Time-based One-Time Password) works by sharing a secret key between your server and the user's authenticator app. The app and server independently generate the same 6-digit code every 30 seconds using that shared secret. You verify the user by comparing what they entered against what your server generated.
The setup flow involves five steps. Each step has details that most tutorials skip.
1️⃣ Generate and store the TOTP secret
Generate a 20-byte cryptographically random secret using crypto.randomBytes(20). Encode it as base32 (the otpauth URI format requires base32). Store the secret encrypted in your database — not in plaintext. The user's authenticator app will need this secret to generate codes, so you can't hash it the way you hash passwords. Encrypt it with AES-256 using a key stored in your secrets manager. The encryption key should be rotatable.
2️⃣ Generate the QR code and provisioning URI
Build an otpauth:// URI in the format: otpauth://totp/{issuer}:{email}?secret={base32secret}&issuer={issuer}&algorithm=SHA1&digits=6&period=30. Generate a QR code from this URI using a library like qrcode. Display it to the user alongside the plain-text secret (for users who can't scan QR codes). The plain-text secret is a sensitive value — treat it like a temporary password and don't store it in your UI state longer than the setup session.
3️⃣ Verify before enabling
Don't enable MFA until the user has successfully verified a code generated by their app. Ask them to enter the current 6-digit code before you mark MFA as active on their account. This confirms the secret was correctly transferred to their authenticator app. A common bug: enabling MFA on the account before verification, then having a user locked out because the transfer failed. Verify first, enable after.
4️⃣ Handle clock skew
TOTP codes are time-dependent. If the user's device clock is slightly off, the code they generate won't match what your server generates. Accept codes from one period before and one period after the current time — this is the standard ±30 second window and is built into most TOTP libraries. Libraries like speakeasy (Node.js) or PyOTP (Python) handle this automatically when you use their verify function with window: 1.
5️⃣ Generate and present recovery codes
Immediately after MFA setup is confirmed, generate 10 recovery codes. These are single-use backup codes the user can enter instead of a TOTP code if they lose access to their authenticator app. Generate each code as a random string (crypto.randomBytes(10).toString('hex') split into readable chunks like XXXX-XXXX-XXXX). Store only the hashed versions in your database — never the codes themselves. Present them to the user once, with a clear instruction to save them securely. You won't be able to show them again.
MFA Enforcement — The B2B Feature Enterprise Customers Require
In a B2B SaaS app, MFA enforcement is an admin feature: an org admin can require all members to set up MFA before they can access the product. This is what most enterprise security teams ask for in vendor questionnaires. It's also what separates a mature SaaS from a consumer app that happens to have optional MFA.
The enforcement flow works like this: when an org admin enables "Require MFA for all members," any member who hasn't set up MFA is blocked from the main app at their next login and redirected to the MFA setup flow. They can't access any product functionality until MFA is configured. Members who already have MFA enabled are unaffected.
Build this as an org-level setting in your admin dashboard: mfa_required: boolean on the organisation table. Check it during the auth middleware — after JWT verification, after role check, add an MFA check: if org.mfa_required and !user.mfa_enabled, redirect to /setup/mfa. This is a clean implementation that doesn't require changing individual user flows.
📌 Give admins a grace period setting
Offer a grace period option — 7 or 14 days — before enforcement kicks in. When an admin turns on MFA enforcement, each member gets a notification and a countdown. After the grace period, access is blocked until MFA is set up. This prevents the situation where an admin enables enforcement and immediately locks out their entire team. Enterprise admins appreciate this — it's a sign of a mature product.
Recovery — The Flow That Most Implementations Get Wrong
Here's the security problem with MFA recovery: if your recovery flow is easy, it becomes a bypass for MFA itself. An attacker who compromises a user's email and knows their password can trigger the recovery flow, skip MFA, and get in. If your recovery flow requires email verification only, MFA isn't protecting against email compromise — which is often the very attack MFA is meant to stop.
Recovery codes are the right answer. When a user enters a recovery code instead of a TOTP code, it works once and is immediately invalidated. The user should then be prompted to set up MFA again. If they've used all their recovery codes, the recovery path must involve manual admin verification — either by a team admin confirming their identity, or by your support team following an identity verification process.
What not to build: an email-based MFA recovery that sends a reset link to the user's email address. This makes MFA trivially bypassable through email compromise. It feels convenient. It eliminates the security value of MFA for anyone whose email is compromised. Don't build it. Recovery codes + admin escalation is the right answer, even if it's less convenient.
Trusted by 500+ startups & agencies
"Hired in 2 hours. First sprint done in 3 days."
Michael L. · Marketing Director
"Way faster than any agency we've used."
Sophia M. · Content Strategist
"1 AI dev replaced our 3-person team cost."
Chris M. · Digital Marketing
Join 500+ teams building 3× faster with Devshire
1 AI-powered senior developer delivers the output of 3 traditional engineers — at 40% of the cost. Hire in under 24 hours.
SMS MFA — Use It, But Know the Risks
SMS-based MFA has real adoption advantages — no app required, familiar to all users, easy to implement via Twilio or AWS SNS. It also has a documented attack vector: SIM swapping, where an attacker convinces a carrier to transfer a phone number to a SIM they control. This allows them to receive any SMS sent to that number — including your OTP codes.
In practice, SIM swapping attacks target high-value accounts. For most B2B SaaS apps, the risk level doesn't warrant avoiding SMS MFA entirely. But if you have customers in finance, crypto, or other high-value contexts — or if your customers specifically request TOTP-only enforcement — give admins the ability to restrict MFA to authenticator apps only and disable SMS as a factor.
Also: never use SMS as the primary MFA method for your own internal admin accounts. Use TOTP or hardware keys for your team. SMS for customers is a UX trade-off. SMS for your internal admin access is an unnecessary risk.
The Bottom Line
Start with TOTP (authenticator app) MFA and add SMS as a lower-security fallback. TOTP satisfies enterprise requirements. SMS improves adoption for less technical users. Don't use SMS for your own internal admin accounts.
Store TOTP secrets encrypted — not hashed. You need to recover the secret to verify codes. Use AES-256 encryption with a key in your secrets manager, not plaintext storage.
Always verify a code before enabling MFA on an account. A common bug is enabling MFA before the user confirms their authenticator is working, resulting in immediate lockout.
Generate 10 single-use recovery codes immediately after MFA setup. Hash them before storage. Present them once. Make recovery codes the only non-admin MFA bypass — email-based recovery bypasses the security benefit of MFA.
Build org-level MFA enforcement for enterprise customers. An admin toggle that requires MFA for all members, with a configurable grace period, is a standard enterprise feature request.
Accept codes from ±30 seconds (window: 1 in most libraries) to handle clock skew. Without this, users with slightly out-of-sync device clocks will intermittently fail verification.
The recovery flow is where most MFA implementations have security gaps. Recovery via email link alone makes MFA bypassable through email compromise — which is often exactly the attack MFA is meant to prevent.
Frequently Asked Questions
How do I add MFA to my SaaS app in 2026?
Implement TOTP first: generate a shared secret, present it as a QR code using an otpauth:// URI, ask the user to verify a code before enabling, generate 10 recovery codes hashed in storage, and enforce MFA at the auth middleware layer. Use a library like speakeasy (Node.js) or PyOTP (Python) for TOTP generation and verification — don't write the algorithm yourself. The full implementation including recovery and org-level enforcement takes 5–8 days of focused engineering.
What's the difference between TOTP and SMS-based MFA?
TOTP generates codes using a shared secret and the current time — the code changes every 30 seconds and doesn't require network access or a carrier. SMS-based MFA sends a one-time code to a phone number via your carrier. TOTP is more secure (not susceptible to SIM swapping) and works offline. SMS is lower friction and doesn't require a separate app. Most B2B SaaS products support both: TOTP as the primary method and SMS as an optional fallback.
How should MFA recovery codes work in a SaaS app?
Generate 10 random recovery codes at MFA setup time, present them once (clearly instructing the user to save them), and store only hashed versions in your database. Each code is single-use — using one immediately invalidates it. If all codes are used or lost, recovery should require admin escalation (team admin confirming identity, or support team identity verification). Never build email-link-based MFA recovery — it makes MFA bypassable through email compromise.
How do I enforce MFA for all users in a B2B SaaS app?
Build an org-level setting (mfa_required boolean on the organisation record) that admins can toggle. When enabled, check this during authentication middleware: after token verification, if the org requires MFA and the user hasn't set it up, block access and redirect to the MFA setup flow. Offer a configurable grace period (7 or 14 days) with notification emails before enforcement blocks access. This is standard in enterprise SaaS and a common vendor questionnaire requirement.
Is SMS-based MFA secure enough for a B2B SaaS app?
It's better than no MFA — significantly. But it has a known attack vector: SIM swapping, where an attacker convinces a carrier to transfer a target's phone number to their control, allowing them to receive OTP codes. For most B2B SaaS apps at early stage, SMS MFA as a fallback option is an acceptable trade-off for adoption. For high-value contexts (fintech, healthcare, crypto), default to TOTP-only and give admins the ability to disable SMS as a factor.
How do I handle clock skew in TOTP verification?
Accept codes from one period before and one period after the current time window — this is the standard ±30 second allowance and is built into most TOTP verification libraries. In speakeasy (Node.js): speakeasy.totp.verify({ secret, encoding: 'base32', token, window: 1 }). This handles users whose device clocks are slightly out of sync without meaningfully reducing security. Don't extend the window beyond 1 — a window of 2 or more creates an unacceptably large validity window for replayed codes.
What's the implementation timeline for adding MFA to an existing SaaS app?
A realistic estimate for a B2B SaaS app with existing JWT authentication: TOTP setup flow (2 days), recovery codes (1 day), authentication middleware update to check MFA (0.5 days), org-level MFA enforcement setting (1 day), SMS fallback via Twilio or AWS SNS (1 day), and QA including lockout and recovery edge cases (1–2 days). Total: 6–8 days for a complete, production-grade implementation. Teams that try to ship it in 2 days usually miss the recovery flow and end up with support tickets about locked-out users.
Add MFA to Your SaaS App Without the 6-Month Wait
devshire.ai matches SaaS teams with pre-vetted developers who've built MFA systems — TOTP, recovery flows, and org-level enforcement — for production B2B apps. Get a shortlist in 48–72 hours and ship the feature in under two weeks.
Find an MFA-Experienced Developer at devshire.ai →
No upfront cost · Shortlist in 48–72 hrs · Freelance & full-time · Stack-matched candidates
About devshire.ai — devshire.ai matches AI-powered engineering talent with SaaS product teams. Every developer in the network has passed a live proficiency screen covering tool use, output validation, and real codebase review. Freelance and full-time options. Typical time-to-hire: 8–12 days. Start hiring →
Related reading: JWT Auth + RBAC in Node.js SaaS · SaaS Security Best Practices · API Security Best Practices · SOC 2 Compliance for Developers · Build a SaaS MVP Fast · How to Add AI Features to Your SaaS
Devshire Team
San Francisco · Responds in <2 hours
Hire your first AI developer — this week
Book a free 30-minute call. We'll match you with the right developer for your project and get you started within 24 hours.
<24h
Time to hire
3×
Faster builds
40%
Cost saved

