Developing Phone Number Authentication with SMS Code
SMS authentication looks simple: request code, get SMS, enter, login. In reality, there are more edge cases than in any other login method. Wrong format for one country, SMS provider limits, stale codes due to delays, code bruteforcing without rate limiting — all happens in production.
Phone Number Input and Validation
Main problem: number formats. Russian number can be entered as +7 999 123-45-67, 89991234567, 7 (999) 123-45-67. Server should accept all and normalize to E.164 (+79991234567). On client — use libphonenumber library (Google), used systemically on Android. For iOS — PhoneNumberKit (Swift wrapper over libphonenumber).
Input field: keyboardType = .phonePad (iOS) / inputType="phone" (Android). Not numberPad — then there's no + button. Real-time number formatting (mask) via UITextField delegate / TextWatcher — user sees +7 (999) 123-45-67 while typing, though E.164 is stored.
Country code selection — either popup with flags and search (full component, 3-5 days) or fixed country if app works in one region only.
OTP Screen: Code Input
Custom OTP input — 4 or 6 separate TextFields with auto-focus advancement per digit. On iOS textContentType = .oneTimeCode enables auto-suggestion from SMS — iOS parses SMS and offers code above keyboard. This is mandatory, users expect it.
On Android SMS automatically read via SmsRetriever API (no permissions needed) or SMS User Consent API (with request). SmsRetriever requires special hash in SMS based on APK signature. On keystore change or debug/release build — hash changes, SMS not auto-read.
// Android — SmsRetriever
val client = SmsRetriever.getClient(context)
val task = client.startSmsRetriever()
task.addOnSuccessListener {
// Register BroadcastReceiver for SMS
}
Countdown timer for retry — standard 60 seconds. Without it users spam "Send again" and clog SMS provider queue. Button unavailable until timer expires.
SMS Provider Choice
Affects delivery and cost:
| Provider | Pros | Cons |
|---|---|---|
| Firebase Auth (SMS) | Free limit, simple integration | No Google Services, Russia limits |
| Twilio Verify | High delivery, global | More expensive |
| SMS.ru / SMSC / Devino | Low price for Russia, ruble accounts | No SDK, only HTTP |
| Yandex 360 SMS | Yandex ecosystem integration | Limited coverage |
For Russian market usually SMS.ru or SMSC with own backend — client never knows provider API key, code request goes to your server, server sends SMS. Backend — rate limiting: max 3 codes per number per hour, max 5 input attempts per code.
Security: What's Mandatory
- Code stored on server in bcrypt hash, not plaintext
- Code TTL: 5-10 minutes, then invalid
- After 3 wrong attempts — block session, request new code
- Rate limiting by IP and phone number — bruteforce and SMS-spam protection
Timeline: 1 to 2.5 weeks. Includes: UI for number and OTP screens, SMS provider integration, code auto-suggestion (iOS + Android), edge case testing (invalid number, expired code, no SMS).







