Best practices for managing retry logic with SMS 2FA
Time to read: 4 minutes
Humans are impatient creatures, so while SMS verification or two-factor authentication (2FA) codes may come through quickly in most parts of the world, we always recommend building retry buffers into verification workflows. This helps prevent:
- Accidentally spamming a user with repeated text messages
- Hitting API rate limits
- Toll fraud or unnecessary spend
While the best practices in this post are written with the Twilio Verify API in mind, many apply regardless of your 2FA provider. Combined with other best practices like building an allow list of country codes to verify, these steps can help make sure your user verification workflow is as seamless as possible.
Launch a demo application with SMS retry best practices
I built an application that shows off the best practices outlined in this post. The application is quick to launch since it's built with Twilio Functions, Twilio's serverless environment. You can launch your own by following these instructions.
Prerequisites include:
- Node.js
- A free Twilio account (sign up for free using this link and receive $10 in credit when you upgrade your account)
- A Verify Service. Create one in the Twilio Console.
Clone or download the sample project on my GitHub:
Install the dependencies with npm install
. Then rename the .env.example
file to .env
(for security, the .env file is part of the .gitignore and won't be committed to source) and fill in the variables with your Account SID and Auth Token, which can be found in the Twilio Console. Fill in the Verify Service SID that you created before:
Launch the project with npm start
and navigate to http://localhost:3000/index.html to test it out.
Best practices for retrying SMS 2FA codes
Implement timeouts on the resend button.
Add a buffer between retries to prevent bad behavior or accidentally sending multiple codes. We recommend starting with 30 seconds.
In the demo, we count down from the max timeout and keep the button disabled until the timeout has expired. For a less animated alternative, you could:
- display the countdown with 5 seconds left
- gray out the resend button until the timeout has expired with copy that states the total buffer (without counting down)
- only display the retry link once the timeout has expired
Track retry attempts
The Verify API includes a list of verification attempts in the response, which you can use to increase the retry buffer with each additional attempt. You can also use the number of attempts to enforce your own rate limits in addition to the Verify API rate limits (5 verification starts in a 10 minute period).
An example of increasing timeout with more attempts can be seen in this function. The default timeout here is the maximum (10 minutes) which can help prevent your application from hitting API rate limits.
Best practices for fallback channels
Offer alternate channels like Voice on the 3rd verification attempt
Voice calling gets priority in telephony networks and can help ensure your customers get a verification code. However, the voice channel can be abused for toll fraud so unless you detect a landline or have a business case for calls, we recommend waiting to expose this channel until the 3rd or 4th attempt to send an SMS has been made or disabling it all together.
Display a "Call me instead" option in your user experience once multiple SMS attempts have been made:
Detect landlines
In addition to using Twilio's Lookup API for detecting invalid phone numbers, you can use the API to detect landline phone numbers and use the call
channel for these numbers instead of defaulting to SMS.
If you enter a landline in the example project it will automatically call instead of text the verification code.
Disable unused channels in the Twilio Console
If you want to disable certain channels altogether, you can do so in the Verify section of the Twilio Console.
Implement reCAPTCHA for voice calls
Implement reCAPTCHA to help detect and prevent bots in your verification flow. Learn more about how to implement this feature in Google's developer documentation.
Adding additional rate limits
The Twilio Verify API supports programmable rate limits that you can apply to particular segments based on the request like an IP address, a geolocation, or a country code.
General user verification best practices
Retry logic is one component of building a seamless user verification workflow. A few other best practices include:
1. Use Twilio's Lookup API to detect invalid numbers and line type before sending a verification
In addition to using Carrier Lookup for identifying landlines, Lookup can be used to identify invalid numbers before you attempt to send a verification code.
2. Build an allow or block list of countries
Using an allow list of countries at sign-up is a great way to ensure you're meeting compliance requirements, reducing fraud, or otherwise controlling your onboarding pipeline.
3. Display complete phone numbers for initial user verification
For phone verification use cases (as opposed to ongoing login or two-factor authentication), display the complete phone number in the interface so the user can detect any typos.
4. Mask phone numbers for ongoing login or two-factor authentication
Once the phone number has been verified the first time, subsequent uses should mask the phone number in order to prevent leaking PII. Unlike the above, there is no option to edit a phone number for ongoing authentication. We recommend exposing 3 or 4 numbers and masking the rest like +1 (5**) ***-**67
or ********567
.
Optional: deploy the project with Twilio Functions
To deploy this project with Twilio Functions you'll need:
Once you install those dependencies, you can deploy this project by running the following command from the verify-retry
folder:
Next steps with user verification
As the usable privacy researcher Miranda Wei has said, we should think about building usable security as a form of "customer service, where the users are trying to achieve security and it's something that is constantly changing. It's not something that you can just set once and forget about." These best practices are a good start, but we recommend monitoring your support costs and user satisfaction to make sure you're providing the best solution as both your product and authentication technology evolves.
For more resources, you might want to check out:
- 5 reasons SMS 2FA isn't going away
- Is email based 2FA a good idea?
- Best practices to secure inbound calls to your contact center
- Quick deploy a One-Time Passcode Verification (OTP) project
I can't wait to see what you build and secure!
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.