Guides / Prompting and shipping
How to Add Two-Factor Authentication to Your App
Add 2FA to your app step by step: authenticator codes vs passkeys vs SMS, enrolment, recovery codes, server-side checks and the mistakes to avoid.
Mythex Team · · 7 min read
To add two-factor authentication (2FA) to your app, let users link an authenticator app by scanning a QR code, ask for the six-digit code it shows after the password, and only create a full session on the server once that code checks out. Give every user single-use recovery codes when they turn it on, limit how many codes someone can guess, and require the password plus a current code before 2FA can be switched off. If you use a hosted auth provider, turn on its built-in multi-factor option instead of building your own.
This guide walks through the choices, the exact flow, prompts you can give an AI builder, and the mistakes that make 2FA look present but do nothing. If the concept is new, start with what two-factor authentication is.
Before you start: you need a working login
2FA sits on top of an existing sign-in. You need one of these first:
- Your own login, with users and hashed passwords stored in your app's database.
- A hosted auth provider that manages users for you.
If you don't have either yet, begin with how to add login to your app. The rest of this guide assumes a password step already works.
Pick your second factor
| Method | User effort | Security | Cost to run | Good default? |
|---|---|---|---|---|
| Authenticator app (TOTP) | Scan a QR code once, type a code at login | Good; phishable if a fake site asks for the code | Free | Yes, for most apps |
| Passkey or security key | Tap or use a fingerprint | Strongest; resists phishing | Free | Yes, if your users are comfortable with it |
| Email code | Check inbox at login | Only as strong as the email account | Email sending costs | As a fallback only |
| SMS code | Wait for a text | Weakest; SIM swapping and phishing | Per-message fees | Only if users can't use an app |
TOTP stands for time-based one-time password, the standard (RFC 6238) that authenticator apps use. Your server and the user's phone share a secret; each generates the same short code from that secret and the current time, usually changing every 30 seconds. No network connection or SMS provider is needed.
Passkeys replace or supplement passwords with a cryptographic key stored on the user's device. They are stronger but more work to build yourself, so many apps start with TOTP and add passkeys later, or get them from an auth provider.
Build it or turn it on?
| Approach | Pros | Cons |
|---|---|---|
| Hosted auth provider's MFA | Less code; the provider maintains the security details | Monthly cost at scale; you depend on the provider's options |
| Your own TOTP with a library | Full control; no per-user fee | You own the edge cases: lockouts, recovery, secret storage |
Check your provider's current documentation for which factors it supports and on which plans. Either way, never write the code-generation maths yourself; use a widely used library.
Decide the policy first
Answer these before you prompt, because they change the design:
- Who must use it? Optional for everyone, required for admins and staff, or required for all. Requiring it for anyone with admin power is common. See role-based access control.
- How often do users re-enter a code? Every login, or once per device for 30 days ("remember this device").
- Which actions need a fresh check? Changing the email, payout details, or turning 2FA off are good candidates. This is called step-up authentication.
- What is the lost-phone process? Recovery codes, plus a manual fallback you are willing to run.
Step by step: TOTP 2FA on your own login
1. Store the right fields
Each user needs: whether 2FA is enabled, the TOTP secret (encrypted at rest, not plain text), the time it was enabled, and a list of recovery codes stored hashed, like passwords. Add a place to count failed attempts.
2. Enrolment
- The user opens Settings and clicks Turn on two-factor authentication.
- The server generates a new secret and returns it as a QR code, plus the text version for people who can't scan.
- The user scans it in an authenticator app and types the current code.
- The server checks the code. Only if it's correct does it save the secret and mark 2FA as enabled.
- The app shows 8–10 recovery codes once and asks the user to save them.
Step 4 matters: switching 2FA on before confirming a code locks out anyone whose scan failed.
3. Login with a second step
- The user submits email and password. The server checks them.
- If 2FA is enabled, the server does not create a full session. It creates a short-lived "second factor pending" state instead.
- The app shows the code screen. The server checks the code, allowing a small window for clock drift.
- Only now does the server issue the real session.
Every protected route and API endpoint must reject the pending state. A 2FA screen that only hides pages in the browser can be skipped by calling the API directly.
4. Limits and alerts
A six-digit code has a million combinations, which is guessable without limits. Cap attempts, lock the second step for a period after repeated failures, and email the user when that happens. See rate limiting and sending emails from your app.
5. Recovery and turning it off
- A recovery code works once, then is deleted.
- Disabling 2FA requires the password and a current code or recovery code.
- Regenerating recovery codes invalidates the old set.
- Email the user whenever 2FA is turned on or off.
Prompts to give your AI builder
Start with the core flow:
Add optional two-factor authentication using authenticator apps (TOTP) to our existing email and password login. Use a well-known TOTP library, don't write the algorithm yourself. In Settings, show a QR code and the text secret, and only enable 2FA after the user enters a valid code. Store the secret encrypted and never show it again after setup. Show 10 single-use recovery codes once and store them hashed.
Then the enforcement, which is where generated code most often falls short:
Enforce 2FA on the server. After a correct password, users with 2FA enabled get a short-lived pending state, not a full session. Every protected page and API route must reject the pending state. Limit code attempts to 5, then lock the second step for 15 minutes and email the user.
Then policy:
Require 2FA for admin accounts: an admin without 2FA is sent to setup and can't reach the dashboard until it's done. Require the password and a current code to turn 2FA off or change the account email. Email the user when 2FA is enabled or disabled.
Finally, ask the agent to explain what it built:
List every route that checks for a completed second factor, where the TOTP secret and recovery codes are stored, and how they're protected.
Common mistakes
- Session issued after the password. The most serious bug. If the full session exists before the code is checked, 2FA is decoration. Test by calling a protected API after the password step only.
- No attempt limit. Unlimited guesses make the code brute-forceable.
- Plain-text secrets or recovery codes. A database leak then hands over everyone's second factor.
- Enabling before verifying. Users who mis-scanned are locked out at next login.
- No recovery path. Every lost phone becomes a support ticket or a lost customer.
- Easy switch-off. If a stolen session can disable 2FA without a code, the protection is gone.
- Codes in logs. Make sure codes and secrets never appear in server logs or error messages.
- Checking only on the login page. Password reset, "magic link" sign-in and any social login must respect 2FA too, or they become a way around it.
Testing checklist
- Enrolment only completes after a valid code
- Login with a correct password but no code gives no access to any protected page or API
- A wrong code is rejected; repeated wrong codes trigger the lockout and an email
- A code from a few minutes ago is rejected
- Each recovery code works exactly once
- Turning 2FA off needs the password and a current code
- Password reset does not bypass 2FA
- Admins without 2FA can't reach admin pages (if required)
- Secrets and recovery codes are encrypted or hashed in the database
- No codes or secrets appear in logs
Run the full security checklist for AI-built apps before launch too.
Adding 2FA in Mythex
Mythex doesn't include a built-in login system for your app's end users, so 2FA is part of whichever auth approach you choose: your own email-and-password login using the project's database, or a hosted provider with multi-factor support. You describe what you want in chat using prompts like the ones above, and any provider keys go in project secrets (through /secrets, project settings or the secrets card the agent shows) rather than in the message box. The docs recipe Add login to your app has a starter prompt, and Security for apps you build covers the habits around keys and permissions. After the agent finishes, test every item on the checklist in Preview before you publish.
Questions
What is the easiest way to add two-factor authentication to an app?
If you use a hosted auth provider, turn on its built-in multi-factor option; that is usually the least code. If you run your own login, add authenticator-app codes (TOTP) with a well-known library, plus recovery codes and a server-side check.
Should I use SMS codes for 2FA?
SMS is better than no second factor, but it is the weakest common option because texts can be redirected through SIM swapping and codes can be phished. Offer an authenticator app as the default, and passkeys if your users are ready for them.
Do I need a database to add 2FA?
If you run your own login, yes. Each user's 2FA secret, whether 2FA is on, and their hashed recovery codes have to be stored somewhere your server can check. A hosted auth provider stores this for you.
What happens if a user loses their phone?
They sign in with one of the single-use recovery codes you showed when they turned 2FA on, then set 2FA up again on a new device. Without recovery codes, you need a manual identity check, so decide that process before launch.