files/en-us/web/api/webotp_api/index.md
{{DefaultAPISidebar("WebOTP API")}}{{SeeCompatTable}}{{securecontext_header}}
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.
SMS 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.
The process works like so:
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 {{jsxref("Promise")}}-based and waits for the SMS message to be received.get() call will fulfill with an {{domxref("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 scheme, port, or other URL features not shown above.
If the get() method is invoked by a third-party site embedded in an {{htmlelement("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 an {{httpheader("Permissions-Policy/otp-credentials", "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 {{htmlelement("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(), {{jsxref("Promise", "promises")}} returned by it will reject with aSecurityError{{domxref("DOMException")}}.
get() call fulfills; includes a code property that contains the retrieved OTP.otp option
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 {{domxref("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) {
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 {{domxref("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}}
{{Compat}}