How to Add Two-Factor Authentication to an AI-Built App

TOTP or SMS, and the recovery-codes step most tutorials skip. A practical guide to adding two-factor authentication to a small, AI-built app.

Steve Jefferson
Steve Jefferson
Developer Advocate
22 August 20261 min read

Two-factor authentication means requiring a second proof of identity beyond a password: a six-digit code from an authenticator app, a text message, or a physical security key. If you're wondering how to add two-factor authentication to an AI-built app, the short version is this: use TOTP (an authenticator app code) as your primary method, treat SMS as an optional convenience rather than your main defense, and generate recovery codes at enrollment so a lost phone doesn't lock someone out of their own account forever. The rest of this covers why that ordering matters for a small app, the actual implementation steps, and the recovery-code detail most quick tutorials leave out entirely.

Why a small app needs this at all

Credential stuffing bots don't check how big your app is before they try it. They run leaked email-password pairs from old, unrelated breaches against every login form they can find, and a solo-built app with two hundred users is just as reachable as one with two hundred thousand. Password reuse, not some hypothetical targeted attacker, is the actual threat model here. A second factor breaks that attack cold: even a correct, breached password is useless without whatever generates the code.

This matters more, not less, once building an app with AI means anyone can ship a working product without ever hiring a security engineer. That speed is the whole appeal, but it also means basic protections like 2FA don't happen unless you deliberately add them. For the wider list of what can go sideways when a non-developer ships a production app, that's covered separately in a rundown of AI risks worth knowing.

TOTP vs SMS: the tradeoff for an indie app

Time-based one-time passwords, TOTP, are generated locally by an app like Google Authenticator, Authy, or 1Password, using a shared secret and the current time. There's no network round trip and nothing for an attacker to intercept in transit, since the code never leaves the user's device until they type it in. The tradeoff is onboarding friction: a user has to install an authenticator app and scan a QR code before they can use it, which is one more step than typing a phone number.

SMS codes feel more familiar. Your app just texts a code to a phone number the user already has. That familiarity comes at a real cost: every message costs money through a provider like Twilio, carrier delays occasionally make codes arrive late or not at all, and SIM-swap fraud is a documented way to intercept SMS codes by porting someone's number to a new device. That's part of why larger platforms have been quietly de-prioritizing SMS as a security method for years, even though it's still common.

For a small or indie app, the practical answer is TOTP as your primary or only method. It's free to run at any volume, doesn't depend on a third-party SMS gateway staying configured and funded, and matches what most technically comfortable users already expect from other apps. SMS only makes sense as an addition if enough of your users would genuinely struggle with scanning a QR code, and even then it belongs as a fallback option, not the default one.

How to actually build it

The implementation shape is the same regardless of your stack. In rough order:

  • Add 2FA as an opt-in step in account settings, not something forced at signup. Users need to already trust the app before they'll bother setting it up.

  • Generate a TOTP secret server-side with a standard library, pyotp, otplib, and speakeasy are common choices, and render it as a QR code using the otpauth:// URI format so any authenticator app can scan it.

  • Require the user to enter one valid code before marking 2FA as enabled. This confirms they scanned it correctly and that their device clock is close enough to yours; TOTP tolerates roughly 30 seconds of drift, not much more.

  • Store the secret encrypted at rest, never in plaintext. The encryption key itself belongs in an environment variable, not committed anywhere in the codebase.

  • On every login, after the password check passes, prompt for the six-digit code and verify it server-side against the stored secret with a small time-window tolerance.

  • Rate-limit verification attempts. A six-digit code space is small enough to brute-force without a limit, so cap it at something like five wrong guesses per short window and lock the attempt, not the whole account.

Where you store that encryption key matters as much as the 2FA logic itself; see how environment variables and secrets should be handled in an AI-built app for the pattern. And if your app already has social login through Google, GitHub, or similar, 2FA sits alongside it rather than replacing it: OAuth providers generally handle their own second factor, so the extra layer you're building here matters most for any email-and-password path you also support.

One thing worth checking before you start: some AI app builders generate a basic auth flow by default but don't include TOTP out of the box. Read through whatever authentication code already exists before assuming 2FA is either present or absent, generated auth scaffolding varies a lot between tools and even between projects from the same tool.

Recovery codes: the step most quick tutorials skip

Here's the gotcha that trips up almost every fast 2FA walkthrough: what happens when someone loses the phone with their authenticator app on it? Without a plan for this, the honest answer is that they're locked out of their own account permanently, because the entire point of TOTP is that the secret never leaves the device it was set up on.

Generate 8 to 10 single-use recovery codes the moment a user enables 2FA. Show them exactly once, tell the user to save them somewhere outside the app itself, a password manager or a printed copy, not a screenshot stored on the same phone that has the authenticator app, and never display them again after that. Hash them the same way you'd hash a password, store only the hashes, and mark each one used the moment it's redeemed so it can't be reused.

Let users regenerate the whole set from account settings, which should invalidate the old batch immediately. And plan for one more fallback beyond the codes: a manual, identity-verified recovery path someone on your side can trigger, even if for the first few hundred users that's just you personally confirming someone's identity over email. Recovery codes handle the common case. The manual path handles the one where someone loses the codes too, and it will happen.

Testing before you ship it

Walk through every path by hand before rolling this out: enrollment with a correct code, enrollment with a wrong code (should fail and leave 2FA off), login with a valid TOTP code, login with an expired or already-used code (should fail), login with a recovery code (should succeed once, then fail if tried again), and what happens when someone changes their password (existing sessions should be invalidated, so a stolen password alone can't ride an old session past the new requirement).

Roll it out as opt-in for a few weeks before making it mandatory for any user role. That gives you room to catch support issues, mostly people who lose codes, before they happen at scale. If you're still working through the fundamentals of accounts and sessions before layering 2FA on top, it's worth getting user accounts right first.

FAQ

Is TOTP more secure than SMS for two-factor authentication?

Generally, yes. TOTP codes are generated on-device and never travel over the phone network, so they aren't exposed to SIM-swap fraud or carrier-level interception, both documented ways to steal SMS codes. TOTP is also free to run at any scale, while SMS costs money per message through a provider.

Do I still need 2FA if my app has social login?

If you also support email-and-password sign-in alongside social login, yes. Social providers like Google or GitHub usually offer their own 2FA for that account, but a leaked password on your app's own login form has nothing to do with the security of the user's Google account, so that path needs its own second factor.

How many recovery codes should I give users?

Eight to ten single-use codes is the common range. Enough that a user has room to lose a few over time without running out, not so many that managing them becomes its own problem.

What happens if a user loses their phone and their recovery codes?

They're stuck without a manual recovery path. This is exactly why a human-verified fallback, even a simple one, matters: recovery codes handle the common case, but someone will eventually lose both the device and the codes at the same time.

Is SMS two-factor authentication free to implement?

No. SMS delivery runs through a paid provider like Twilio or a similar gateway, and the cost scales with usage. TOTP has no per-verification cost, which is a real factor for a small app watching every recurring bill.

How did this land?

About the author

Steve Jefferson
Steve Jefferson

Developer Advocate

Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.