Learn Measure Blog Live About
A drawing of a woman using OTP to log in to a web app.

Verify phone numbers on the web with the Web OTP API

Help users with OTPs received through SMS

Updated

What is the Web OTP API?

These days, most people in the world own a mobile device and developers are commonly using phone numbers as an identifier for users of their services.

There are a variety of ways to verify phone numbers, but a randomly generated one-time password (OTP) sent by SMS is one of the most common. Sending this code back to the developer's server demonstrates control of the phone number.

The Web OTP API was originally called the SMS Receiver API. You may still see it named that way in some places. If you used that API, you should still read this article. There are significant differences between the current and earlier versions of the API.

This idea is already deployed in many scenarios to achieve:

  • Phone number as an identifier for the user. When signing up for a new service, some websites ask for a phone number instead of an email address and use it as an account identifier.
  • Two step verification. When signing in, a website asks for a one-time code sent via SMS on top of a password or other knowledge factor for extra security.
  • Payment confirmation. When a user is making a payment, asking for a one-time code sent via SMS can help verify the person's intent.

The current process creates friction for users. Finding an OTP within an SMS message, then copying and pasting it to the form is cumbersome, lowering conversion rates in critical user journeys. Easing this has been a long standing request for the web from many of the largest global developers. Android has an API that does exactly this. So does iOS and Safari.

The Web OTP API lets your app receive specially-formatted messages bound to your app's origin. From this, you can programmatically obtain an OTP from an SMS message and verify a phone number for the user more easily.

Warning: Attackers can spoof SMS and hijack a person's phone number. Carriers can also recycle phone numbers to new users after an account is closed. While SMS OTP is useful to verify a phone number for the use cases above, we recommend using additional and stronger forms of authentication (such as multiple factors and the Web Authentication API to establish new sessions for these users.

Current status

The table below explains the current status of the Web OTP API.

Step Status
1. Create explainer Complete
2. Create initial draft of specification Complete
3. Gather feedback and iterate on design Complete
4. Origin trial Complete
5. Launch Chrome 84

Changes from earlier versions

Early versions of this API were called SMS Receiver. If you are famillar with that version of the API be aware of the changes made to it. Improvements from SMS Receiver API include:

  • The SMS message format is now aligned with WebKit's.
  • The web page only recives an OTP code regardless of whatever else is in the message.
  • The browser's application hash code is no longer required in the message.

See it in action

Let's say a user wants to verify their phone number with a website. The website sends a text message to the user over SMS and the user enters the OTP from the message to verify the ownership of the phone number.

With the Web OTP API, these steps are as easy as one tap for the user, as demonstrated in the video. When the text message arrives, a bottom sheet pops up and prompts the user to verify their phone number. After clicking the Verify button on the bottom sheet, the browser pastes the OTP into the form and the form is submitted without the user needing to press Continue.

The whole process is diagrammed in the image below.

Web OTP API diagram

Try the demo yourself. It doesn't ask for your phone number or send an SMS to your device, but you can send one from another device by copying the text displayed in the demo. This works because it doesn't matter who the sender is when using the Web OTP API.

  1. Go to https://web-otp-demo.glitch.me in Chrome 84 or later.
  2. Press Copy to copy the text message.
  3. Using your SMS app send it to another phone.
  4. Press Verify.
  5. From the other phone, send yourself the copied text message via SMS.

Did you receive the SMS and see the prompt to enter the code to the input area? That is how the Web OTP API works for users.

Caution:

  • If the sender's phone number is included in the receiver's contact list, this API will not be triggered due to the design of the underlying SMS User Consent API.
  • If you are using a work profile on your Android device and the Web OTP does not work, try installing and using Chrome on your personal profile instead (i.e. the same profile in which you receive SMS messages).

Use the Web OTP API

Using the Web OTP API consists of two parts: JavaScript in your web app and formatted message text sent via SMS. I'll cover the JavaScript first.

The Web OTP API requires a secure origin (HTTPS).

Feature detection

Feature detection is the same as for many other APIs:

if ('OTPCredential' in window) {
...
}

Process the OTP

The Web OTP API itself is simple enough. Use navigator.credentials.get() to obtain the OTP. Web OTP adds a new otp option to that method. It only has one property: transport, whose value must be an array with the string 'sms'.

const content = await navigator.credentials.get({
otp: {
transport:['sms']
}
});

This triggers the browser's permission flow when an SMS message arrives. If permission is granted, the returned promise resolves with an OTPCredential object.

{
code: "123456" // Obtained OTP
type: "otp" // `type` is always "otp"
}

Next, pass the OTP value to an input field and submit it on behalf of the user.

document.querySelector('#input').value = content.code;

Aborting the message

To set a timeout that aborts the get() call, pass an AbortController instance in the options object.

const abortController = new AbortController();
let timer = setTimeout(() => {
abortController.abort();
}, 10 * 1000);

const content = await navigator.credentials.get({
otp: { transport:['sms'] },
signal: abortController.signal
});

Use the API declaratively

The code below demonstrates a Web Component that extends input.

if ('customElements' in window && 'OTPCredential' in window) {
customElements.define("one-time-code",
class extends HTMLInputElement {
connectedCallback() {
this.abortController = new AbortController();
this.receive();
}
disconnectedCallback() {
this.abort();
}
abort() {
this.abortController.abort();
}
async receive() {
try {
const content = await navigator.credentials.get({
otp: {transport:['sms']}, signal: this.abortController.signal
});
this.value = content.code;
this.dispatchEvent(new Event('autocomplete'));
} catch (e) {
console.error(e);
}
}
}, {
extends: "input"
});
}

After this declaration you can add is="one-time-code" to any input element. As soon as the element is added to the document tree, it starts waiting for SMS messages to arrive and emits an autocomplete event as soon as an OTP is passed.

<form>
<input is="one-time-code" autocomplete="one-time-code" required/>
<input type="submit">
</form>
const otp = document.querySelector('#otp');
otp.addEventListener('autocomplete', e => {
this.form.submit();
});

Format the SMS message

The API itself should look simple enough, but a critical part is to format your SMS text message according to a specific convention. The message has to be sent after navigator.credentials.get() is called and must comply with the formatting convention.

The SMS message must be received on the device where navigator.credentials.get() was called.

The message must adhere to the following formatting:

  • The host part of the URL of the website that invoked the API must be preceded by @.
  • The URL must contain a pound sign ('#') followed by the OTP.
  • Optionally, the message may contain additional text for the user.

For example:

Your OTP is: 123456.

@www.example.com #123456

Demos

Try various messages with the demo: https://web-otp-demo.glitch.me

You may also fork it and create your version: https://glitch.com/edit/#!/web-otp-demo.

Problem with the implementation?

Did you find a bug with Chrome's implementation?

  • File a bug at https://new.crbug.com. Include as much detail as you can, simple instructions for reproducing, and set Components to Blink>SMS.

Planning to use the API?

Are you planning to use the Web OTP API? Your public support helps us prioritize features, and shows other browser vendors how critical it is to support them. Send a Tweet to @ChromiumDev with #webotp and let us know where and how you're using it.

Differences from SMS Receiver API

Consider Web OTP API an evolved version of the SMS Receiver API. Web OTP API has a few significant differences compared to the SMS Receiver API.

  • The expected text format for the SMS message has changed.
  • It no longer requires an app hash string to be included in the SMS message.
  • The method called is now navigator.credentials.get() rather than navigator.sms.receive().
  • The get() receives only the OTP rather than the entire SMS message as receive() did before.
  • It's now possible to abort the call to get().

FAQ

Is this API compatible between different browsers?

Chromium and WebKit agreed on the SMS text message format. Find WebKit's documentation here: Delivering origin-bound one-time codes over SMS

Is it safe to use SMS as a way to authenticate?

While SMS OTP is useful to verify a phone number when the number is first provided, phone number verification via SMS must be used carefully for re-authentication since phone numbers can be hijacked and recycled by carriers. Web OTP is a convenient re-auth and recovery mechanism, but services should combine it with additional factors, such as a knowledge challenge, or use the Web Authentication API for strong authentication.

Find more questions at the FAQ section in the explainer.

Last updated: Improve article