Why a single password is not enough
A crypto exchange user lost access to their account due to SIM-swap — an attacker reissued the SIM card and intercepted SMS codes. The account was drained in 15 minutes. We encountered this on one project: a client lost $200,000 because two-factor authentication was tied to SMS. After that, we embedded TOTP 2FA into all crypto apps we develop.
TOTP (Time-based One-Time Password, RFC 6238) is a standard implemented by Google Authenticator, Authy, and most enterprise 2FA apps. For a crypto app, this is the minimum requirement: SMS codes are intercepted via SIM-swap, TOTP is not. We guarantee our implementation protects against these attacks.
How TOTP works
The algorithm: HMAC-SHA1 of the current time (30-second window) and a shared secret. The shared secret is generated during 2FA setup and stored both on the server and on the user's device (in the authenticator app). No network transmission during each authentication — only verification of the computed code.
On the backend, verification uses popular libraries: pyotp (Python), otplib (Node.js), google-authenticator (Java). On the mobile client, the code is manually entered from the authenticator app — no additional TOTP logic needed. TOTP works 10 times faster than push notifications because it requires no internet connection.
Why TOTP is safer than SMS and push notifications?
| Parameter | TOTP | SMS | Push notification |
|---|---|---|---|
| Network dependency | No | Yes | Yes |
| Phishing vulnerability | Low | Medium | High |
| Cost for developer | Free | Carrier charges | Free |
| User convenience | Medium | High | High |
| Security under SIM-swap | Absolute | None | Depends on display |
TOTP is the gold standard for financial apps. Push notifications can be intercepted by malware on the device, SMS via SIM-swap. TOTP requires physical access to the device with the authenticator app. OWASP recommends TOTP for protecting critical accounts.
Embedding 2FA into the login process
The classic flow: login/password → backend check → if the user has 2FA active, backend returns an intermediate token → mobile shows the TOTP input screen → backend verifies the code and issues a full JWT.
sealed class LoginState { object Idle : LoginState() object Loading : LoginState() data class TwoFactorRequired(val tempToken: String) : LoginState() data class Success(val authToken: String) : LoginState() data class Error(val message: String) : LoginState() } class LoginViewModel(private val authRepository: AuthRepository) : ViewModel() { val state = MutableStateFlow<LoginState>(LoginState.Idle) fun submitTOTP(code: String, tempToken: String) { viewModelScope.launch { state.value = LoginState.Loading authRepository.verifyTOTP(code, tempToken) .onSuccess { token -> state.value = LoginState.Success(token) } .onFailure { e -> state.value = LoginState.Error( if (e is InvalidCodeException) "Invalid code" else "Server error" ) } } } } How to set up 2FA: step-by-step guide
- User initiates 2FA setup in profile.
- Backend generates a shared secret and QR-URI
otpauth://totp/.... - Mobile app displays the QR code (on Android —
zxing-android-embedded, on iOS —CoreImage.CIFilter.qrCodeGenerator) and a text secret for manual entry. - User scans the QR with Google Authenticator or Authy.
- For verification, user enters the first generated code.
- Backend checks the code and activates 2FA.
- After activation, 8-10 one-time backup codes are generated and displayed on screen with an option to copy or download.
Backup codes: what are they and why are they needed?
Without backup codes, 2FA in a crypto app risks irreversible loss of access. We generate 8-10 one-time codes during 2FA activation. The backend stores them as bcrypt hashes. The mobile app shows them once with the ability to copy or download as a text file.
Implementing backup codes requires a separate branch in the login flow: if the TOTP input field is present, allow switching to "Use backup code". After use, the code is invalidated, and the user receives a warning about the remaining count.
| Storage method | Accessibility | Security |
|---|---|---|
| Paper | High | Low (loss) |
| Password manager | High | High |
| Screenshot on device | Medium | Medium |
More about protecting the code input screen
On Android, `FLAG_SECURE` is used to block screenshots. On iOS, the content is hidden when switching apps via `applicationWillResignActive`. The code input field should have `isSecureTextEntry = true` to avoid autofill and leakage through third-party keyboards.What is included in the work
- Backend: TOTP verification, QR-URI generation, backup code storage (bcrypt) and secret storage (encrypted), API for setup and reset of 2FA.
- Mobile: TOTP input screen, onboarding with QR, screenshot protection, backup code display.
- Documentation: flow description, security scheme, user instructions.
- CI/CD: automated code signing and distribution via TestFlight / Firebase App Distribution.
- Support: 2 weeks after launch, bug fixes.
The cost of a single breach for a crypto company can reach $100,000, so investing in 2FA is justified. Savings from implementing TOTP over SMS amount to about $15,000 per year per 10,000 users.
Timelines and cost
TOTP 2FA integration (setup, login flow, backup codes, screen protection) — 1-2 weeks. Cost is calculated individually, depending on the current architecture and backend modifications needed. We have evaluated 15+ projects with cryptocurrency themes — reach out, let's discuss your case.
We are a mobile development team with years of experience. We have implemented 2FA for dozens of crypto exchanges and wallets. We guarantee compliance with App Store Review Guidelines (Section 4.2/5.1) and Google Play policies. Contact us for a consultation and technical audit of your current login system. Order TOTP implementation today — protect users' assets from SIM-swap attacks.







