Limited availability
Experimental: This is an experimental technology
Check the Browser compatibility table carefully before using this in production.
Secure context: This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.
The WebOTP API provides a streamlined user experience for web apps to verify that a phone number belongs to a user when using it as a sign-in factor. WebOTP is an extension of the Credential Management API.
The verification is done via a two-step process:
Phone numbers are often used as a way to identify the user of an app. An SMS is frequently deployed to verify that the number belongs to the user. The SMS typically contains an OTP that the user is required to copy and paste into a form in the app to verify that they own the number. This is a somewhat clunky user experience.
OTP use cases include:
The WebOTP API allows web apps to expedite this validation process by copying the OTP from the SMS and passing it to the app automatically after the user has provided consent (most native platforms have an equivalent API).
Note that an OTP is bound to the sending domain. This is a useful security constraint for verifying that the OTP is coming from the right source, which can mitigate the risk of phishing attacks during day-to-day reauthentication.
Security concerns with SMS OTPsSMS OTPs are useful for verifying phone numbers, and using SMS for a second factor is certainly better than having no second factor. In some regions, other identifiers such as email addresses and authenticators are not widely-used, so SMS OTPs are very common.
However, SMSes aren't that secure. Attackers can spoof an SMS and hijack a person's phone number. Carriers can recycle phone numbers to new users after an account is closed.
You are, therefore, recommended to use a stronger form of authentication if possible, such as a Web Authentication API-based solution involving a password and security key or a passkey.
How does the WebOTP API work?The process works like so:
navigator.credentials.get()
with an otp
option specifying a transport
type of "sms"
. This triggers a request for an OTP from the underlying system, the source of which will be a specially-formatted SMS message (containing the OTP and the app's domain) received from the app server. The get()
call is Promise
-based and waits for the SMS message to be received.get()
call will fulfill with an OTPCredential
object containing the OTP.A typical SMS message looks like so:
Your verification code is 123456. @www.example.com #123456
@
.#
).Note: The provided domain value must not include a URL schema, port, or other URL features not shown above.
If the get()
method is invoked by a third-party site embedded in an <iframe>
, the SMS structure should be:
Your verification code is 123456. @top-level.example.com #123456 @embedded.com
In this case, the last line must consist of:
@
.#
).@
.The availability of WebOTP can be controlled using a Permissions Policy specifying a otp-credentials
directive. This directive has a default allowlist value of "self"
, meaning that by default, these methods can be used in top-level document contexts.
You could specify a directive allowing the use of WebOTP in a specific cross-origin domain (i.e., inside an <iframe>
) like this:
Permissions-Policy: otp-credentials=(self "https://embedded.com")
Or you could specify it directly on the <iframe>
like this:
<iframe src="https://embedded.com/..." allow="otp-credentials"> ... </iframe>
Note: Where a policy forbids use of WebOTP get()
, promises
returned by it will reject with a SecurityError
DOMException
.
OTPCredential
Returned when a WebOTP get()
call fulfills; includes a code
property that contains the retrieved OTP.
CredentialsContainer.get()
, the otp
option
Calling get()
with an otp
option instructs the user agent to attempt to retrieve an OTP from the underlying system's SMS app.
In this example, when an SMS message arrives and the user grants permission, an OTPCredential
object is returned with an OTP. This password is then prefilled into the verification form field, and the form is submitted.
The form field includes an autocomplete
attribute with the value of one-time-code
. This is not needed for the WebOTP API to work, but it is worth including. As a result, Safari will prompt the user to autofill this field with the OTP when a correctly-formatted SMS is received, even though the WebOTP API isn't fully supported in Safari.
<input type="text" autocomplete="one-time-code" inputmode="numeric" />
The JavaScript is as follows:
// Detect feature support via OTPCredential availability
if ("OTPCredential" in window) {
window.addEventListener("DOMContentLoaded", (e) => {
const input = document.querySelector('input[autocomplete="one-time-code"]');
if (!input) return;
// Set up an AbortController to use with the OTP request
const ac = new AbortController();
const form = input.closest("form");
if (form) {
// Abort the OTP request if the user attempts to submit the form manually
form.addEventListener("submit", (e) => {
ac.abort();
});
}
// Request the OTP via get()
navigator.credentials
.get({
otp: { transport: ["sms"] },
signal: ac.signal,
})
.then((otp) => {
// When the OTP is received by the app client, enter it into the form
// input and submit the form automatically
input.value = otp.code;
if (form) form.submit();
})
.catch((err) => {
console.error(err);
});
});
}
Another good use for the AbortController
is to cancel the get()
request after a certain amount of time:
setTimeout(() => {
// abort after 30 seconds
ac.abort();
}, 30 * 1000);
If the user becomes distracted or navigates somewhere else, it is good to cancel the request so that they don't get presented with a permission prompt that is no longer relevant to them.
Specifications Browser compatibility See alsoRetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4