Creating a SMS OTP form

Joel Oliveira
Joel Oliveira
Dec 18 2020
Posted in Engineering & Technology

How to optimize UX for SMS verification

Creating a SMS OTP form

It's a very common practice to use SMS to provide users with a one time password (OTP) for a variety of scenarios. A few examples for these types of user experiences include:

Two-factor authentication

Besides providing a username and password, more and more websites also require a OTP to confirm the identity of a user before starting a session. Although SMS could be considered for this scenario, be aware that is not the most secure method of authentication. Phone numbers can be recycled or highjacked and the whole OTP concept is also not immune to phishing attacks.

Phone number verification

Some services make use of a phone number as the primary identifier of an account. In such services, it makes total sense to use SMS OTP to verify a phone number.

Account recovery

Pretty much every online business that provides users with some form of authentication will need a way to allow them to recover a lost password. Sending an email message or a text message are very common account recovery methods.

Purchase confirmation

Some payment systems, banks or credit card issuers, use SMS OTP to provide an extra layer of security before processing payments.

In this post, I will explore how one could build a web based form that can handle any of the above cases. By taking advantage of the latest developments in all major browsers, providing the best user experience can highly be improved with a couple of small steps.

Using an input element

The easiest way to collect an OTP is by using an input element. Even if some of the solutions in this post won't work in all browsers, users will still be able to type and submit the form. A good example of a form would be the following:

<form action="/verify" method="POST">
  <input type="text"
         inputmode="numeric"
         autocomplete="one-time-code"
         pattern="\d{6}"
         required>
</form>

Let's break down the input attributes and elaborate on why these are the best choices for what we are trying to achieve.

type="text"

In most cases an OTP is a 6 digit code and it might seem logical to use type="number" as it would automatically use a numeric keyboard. But for this purpose, this might actually create undesirable side effects. Because an input of type number will expect a countable number, browsers might remove preceding zeros. It will also cause the field to have up and down buttons that can be pressed to increment and decrement the value, which can be pretty annoying for users.

inputmode="numeric"

Using this attribute will simply turn the keyboard into a numeric keyboard. Until very recently, most websites would simply use type="number" or even type="tel" to hack around the numeric keyboard due to the lack of support in some browsers. But with Firefox also supporting inputmode="numeric" this is no longer necessary.

autocomplete="one-time-code"

This attribute will allow developers to indicate what kind of autocomplete assistance the browser should provide. Whenever a user receives a text message while a form is opened, the operating system will suggest the OTP to the user. This is something that is only supported by Safari 12 in iOS, iPadOS and macOS and will improve substantially the user experience in these platforms.

Other optional attributes, like pattern and required, can be used to make sure your form can only be submitted if the OTP is provided and it has the desired format.

Formatting the text message

To take the user experience to a whole new level, you should follow the Origin-bound one-time codes delivered via SMS specification.

It is a very simple formatting rule: Finish the SMS message with the bound domain preceded with @ and the OTP preceded with #. For example:

Your OTP is 123456

@notificare.com #123456

This will ensure that extraction of these codes is easier and reliable. It will also make it harder to trick users into providing these in malicious websites. Browsers will automatically suggest an OTP if they are in a page in the corresponding domain. In the example below, you can see how Safari in iOS 14 or later will suggest the OTP received via SMS:

Use the Web OTP API

Browsers like Chrome, Opera and Vivaldi on Android also support the origin-bound one-time codes rule with the Web OTP API. By calling navigator.credentials.get(), a website will wait for a text message that complies with this specification to be delivered after permission is granted by the user. This will allow a developer to receive the OTP via JavaScript and prefill the input element or submit the form automatically.

if ('OTPCredential' in window) {
  window.addEventListener('DOMContentLoaded', e => {

    const input = document.querySelector('input[autocomplete="one-time-code"]');

    if (!input) {
      return;
    }

    const ac = new AbortController();
    const form = input.closest('form');

    if (form) {
      form.addEventListener('submit', e => {
        ac.abort();
      });
    }

    navigator.credentials.get({
      otp: { transport:['sms'] },
      signal: ac.signal
    }).then(otp => {
      input.value = otp.code;
      if (form) {
	form.submit();
      }
    }).catch(err => {
      console.log(err);
    });

  });
}

By using the code above, the Web OTP API, which requires a secure origin (HTTPS), will automatically handle an OTP if permission is granted by the user, as shown in this example:

Where to go from here?

These same principles can also improve the user experience in native mobile apps. Both iOS and Android support SMS OTP access with very few tweaks and similar configurations. Implementing such concepts do not only improve considerably the authentication flow in your app but it will also substantially increase security when using SMS OTP.

As always, if you have any questions, suggestions or any correction to this post, feel free to drop a message in our Support Channel.

Keep up-to-date with the latest news