You are launching a mobile application with sensitive data—finances, corporate documents, personal correspondence. Login and password are no longer considered sufficient protection: database leaks, phishing, and password reuse are daily realities. Two-factor authentication (2FA) closes these risks by adding a second factor after credential entry. But which method to choose and how to implement without sacrificing UX? We have implemented 2FA for 20+ apps with a total audience of 2 million users and are sharing our experience.
Which 2FA method to choose
TOTP — the user scans a QR code in an authenticator app (Google Authenticator, Microsoft Authenticator). It generates a 6-digit code every 30 seconds. Standard RFC 6238. No internet required, no SMS dependency, free. Best choice for a tech-savvy audience.
SMS OTP — code arrives via SMS. Familiar to mass audiences, but vulnerable to SIM swapping, dependent on coverage, costly for bulk sending. For B2C, often the only intuitively understood option.
Push notification with confirmation — on login attempt, the user receives a push requesting "Confirm login?" (Yes/No). Implemented via FCM/APNs. Good UX, but push deliverability is critical.
Email OTP — an alternative to SMS, higher deliverability, but lower urgency feeling.
| Method | Security | UX | Cost | Dependencies |
|---|---|---|---|---|
| TOTP | High | Medium (requires app installation) | Zero | No internet after setup |
| SMS OTP | Low (SIM swapping) | High (everyone knows it) | $0.05–0.15 per SMS | Cellular network operation |
| Push notification | Medium | High (one tap) | Push infrastructure cost | Push deliverability |
TOTP is 10 times more secure than SMS (no SIM swapping) and matches push with proper trusted device configuration.
Why TOTP is the best choice for security
TOTP eliminates dependency on communication channels: the code is generated locally on the device, cannot be intercepted or altered in transit. Unlike SMS, where SIM swapping is possible, or push, which requires a stable internet connection, TOTP works offline. The average cost of a single SMS is $0.05–0.15 — with mass sending, this amounts to millions. TOTP is free and requires no infrastructure.
TOTP implementation
On the server, when enabling 2FA, we generate a secret (20 bytes of random data in Base32). We form otpauth://totp/AppName:[email protected]?secret=BASE32SECRET&issuer=AppName — this is the URI for the QR code.
# Python — secret generation and code verification import pyotp secret = pyotp.random_base32() # Save to DB, bound to user totp = pyotp.TOTP(secret) # Verify entered code is_valid = totp.verify(user_input_code, valid_window=1) # valid_window=1 allows codes ±30 seconds from current — compensates clock drift The QR code is generated on the server as PNG and delivered to the client via a protected endpoint (only for authorized user). It is shown once during setup — re-displaying the QR is insecure.
On the mobile client — a 6-digit code input screen, similar to OTP field from SMS authorization. Auto-submit on entering the 6th digit.
Backup codes
During TOTP setup, we always generate 8–10 one-time backup codes. If the user loses the phone with Authenticator, only these allow login. Show them once, suggest saving. Store as bcrypt hashes. Without backup codes, losing the phone means losing access forever. We include this option by default.
How to safely implement 2FA disabling
Allowing a user to disable 2FA must be done carefully. Minimum: confirmation with the current password + current 2FA code. Otherwise, an attacker with session access can disable protection. We also log such actions and send an email notification.
Changing TOTP device: we generate a new secret, keeping the old one active for an additional 5 minutes (migration window), then invalidate it.
Trusted device mechanism
// iOS — device token generation after successful 2FA let deviceToken = UUID().uuidString let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "device_token", kSecAttrService as String: "com.yourapp.auth", kSecValueData as String: deviceToken.data(using: .utf8)!, kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock ] SecItemAdd(query as CFDictionary, nil) On Android — EncryptedSharedPreferences with AndroidKeyStore key for encrypting the device token.
Device token is passed on login. Server checks: if token exists and matches user — 2FA is not required. Trusted device lifetime: 30–90 days, after which re-2FA is required.
Comparison of implementation approaches
| Criteria | TOTP | SMS OTP | Push-2FA |
|---|---|---|---|
| Implementation time | 2–3 weeks | 1–2 weeks | 1.5–2 weeks |
| Complexity | Medium | Low | Medium |
| Security | High | Low | Medium |
| UX | Requires app | Familiar | Simple tap |
We have 5+ years of experience in mobile development and 50+ projects with 2FA implemented. We will evaluate your project and offer the optimal solution.
What is included in the work
We provide a complete package:
- Documentation: architecture description, flow diagram, support manual
- Source code of the 2FA module (iOS/Android/Backend)
- Setup of backup codes and recovery mechanism
- Integration with your existing authentication system
- Assistance with App Store and Google Play publishing (taking into account App Store Review Guidelines Section 5.1.1)
- Guarantee of stable operation for 30 days after deployment
- Consultation on trusted devices setup and method selection
Timelines: from 1.5 to 3 weeks depending on complexity. TOTP with backup codes and trusted devices is near the upper bound. SMS 2FA without device trust takes about a week.
Contact us — we will discuss details and prepare a turnkey proposal. Get a consultation and see the quality of our solutions.







