WebOTP API

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

实验性: 这是一项实验性技术
在生产中使用此技术之前,请仔细检查浏览器兼容性表格

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,且支持此功能的浏览器数量有限。

WebOTP API 提供了一种简化的用户体验,让 Web 应用程序可以在用户使用手机号码作为登录因素时,验证手机号码是否属于用户。WebOTP 是 Credential Management API 的一个扩展。

验证通过一个两步过程完成。

  1. 应用客户端请求一个一次性密码(OTP),该 OTP 来自应用服务器发送的一个特殊格式的短信。
  2. 使用 JavaScript 将 OTP 输入到应用客户端的验证表单中,然后将其提交回服务器以验证是否与短信中原始发送的内容匹配。

概念与用法

电话号码通常被用作识别应用用户的标识。短信经常被用来验证该号码是否属于用户。短信通常包含一个 OTP,用户需要复制并粘贴到应用中的表单中,以验证他们拥有该号码。这是一种相当笨拙的用户体验。

OTP 的使用场景包括:

  • 通过使用电话号码作为 多因素身份验证 系统的一部分来提高登录安全性。
  • 验证敏感操作,例如付款。

WebOTP API 允许 Web 应用程序通过自动复制短信中的 OTP 并将其传递给应用程序来加快此验证过程,前提是用户已提供同意(大多数原生平台都有等效的 API)。

请注意,OTP 绑定到发送域。这是一个有用的安全限制,用于验证 OTP 是否来自正确的来源,这可以减轻日常重新验证期间网络钓鱼攻击的风险。

短信 OTP 的安全顾虑

短信 OTP 对于验证电话号码很有用,并且使用短信作为第二个因素肯定比没有第二个因素要好。在某些地区,电子邮件地址和身份验证器等其他标识符并未得到广泛使用,因此短信 OTP 非常普遍。

然而,短信的安全性不高。攻击者可以伪造短信并劫持一个人的电话号码。运营商可以在账户关闭后将电话号码回收给新用户。

因此,建议您如果可能,使用更强的身份验证形式,例如基于 Web Authentication API 的解决方案,包括密码和安全密钥或通行密钥。

WebOTP API 如何工作?

过程如下:

  1. 在需要电话号码验证的节点,应用客户端会要求用户在表单中输入他们的电话号码,然后将其提交给应用服务器。
  2. 应用客户端然后调用 navigator.credentials.get(),并附带一个 otp 选项,指定 transport 类型为 "sms"。这会触发从底层系统请求 OTP,其来源是一个从应用服务器接收到的特殊格式的短信(包含 OTP 和应用的域名)。get() 调用是基于 Promise 的,并等待接收短信。
  3. 应用服务器将短信发送到指定的电话号码。这必须在步骤 2 发生后立即进行。
  4. 当设备收到短信时,如果其中包含应用的域名,浏览器会询问用户是否同意检索/使用 OTP。例如,Chrome 会显示一个对话框,请求用户允许从短信中检索 OTP;其他浏览器可能处理方式不同。如果用户同意,get() 调用将以一个包含 OTP 的 OTPCredential 对象返回。
  5. 然后,您可以按需使用 OTP。典型用法是将其设置为应用客户端验证表单的值,然后提交表单,使过程尽可能无缝。
  6. 应用服务器然后验证回传给它的 OTP 是否与它最初在短信中发送的一致,如果一致,则完成过程(例如,登录用户)。

短信格式

典型的短信看起来像这样:

Your verification code is 123456.

@www.example.com #123456
  • 第一行和第二行空白行是可选的,用于人类可读性。
  • 最后一行是强制性的。如果存在其他行,它必须是最后一行,并且必须由以下部分组成:
    • 调用 API 的网站 URL 的域名部分,前面加上一个 @
    • 后面跟着一个空格。
    • 后面跟着 OTP,前面加上一个井号(#)。

注意:提供的域名值不得包含 URL 方案、端口或其他未在上方显示的 URL 要素。

如果 get() 方法由嵌入在 <iframe> 中的第三方网站调用,则短信结构应为:

Your verification code is 123456.

@top-level.example.com #123456 @embedded.com

在这种情况下,最后一行必须由以下部分组成:

  • 顶级域的域名部分,前面加上一个 @
  • 后面跟着一个空格。
  • 后面跟着 OTP,前面加上一个井号(#)。
  • 后面跟着一个空格。
  • 后面跟着嵌入域的域名部分,前面加上一个 @

控制对 API 的访问

WebOTP 的可用性可以使用 Permissions Policy 进行控制,该策略指定了 otp-credentials 指令。此指令的默认允许列表值为 "self",这意味着默认情况下,这些方法可以在顶层文档上下文中用于。

您可以指定一个指令,允许在特定的跨域域中使用 WebOTP(即,在 <iframe> 中),如下所示:

http
Permissions-Policy: otp-credentials=(self "https://embedded.com")

或者您也可以直接在 <iframe> 上指定,如下所示:

html
<iframe src="https://embedded.com/..." allow="otp-credentials"> ... </iframe>

注意:在策略禁止使用 WebOTP get() 的地方,它返回的 Promise 将会因 SecurityError DOMException 而拒绝。

接口

OTPCredential

WebOTP get() 调用成功时返回;包含一个 code 属性,其中包含检索到的 OTP。

其他接口的扩展

CredentialsContainer.get()otp 选项

使用 otp 选项调用 get() 会指示用户代理尝试从底层系统的短信应用中检索 OTP。

示例

在此示例中,当收到短信并用户授予权限后,会返回一个带有 OTP 的 OTPCredential 对象。然后,此密码会预填充到验证表单字段中,并提交表单。

尝试使用手机进行此演示.

表单字段包含一个值为 one-time-codeautocomplete 属性。这对于 WebOTP API 的工作不是必需的,但值得包含。因此,即使 WebOTP API 在 Safari 中不受完全支持,当收到格式正确的短信时,Safari 也会提示用户使用 OTP 自动填充此字段。

html
<input type="text" autocomplete="one-time-code" inputmode="numeric" />

JavaScript 如下:

js
// 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);
    });
}

另一个使用 AbortController 的好方法是,在一段时间后取消 get() 请求。

js
setTimeout(() => {
  // abort after 30 seconds
  ac.abort();
}, 30 * 1000);

如果用户分心或导航到其他地方,取消请求很重要,这样他们就不会看到不再相关的权限提示。

规范

规范
WebOTP API

浏览器兼容性

另见