SMS OTP 表单最佳实践

了解如何优化您的 SMS OTP 表单并改善用户体验。

要求用户提供通过短信 (SMS) 传送的一次性密码 (OTP) 是确认用户电话号码的常用方法。SMS OTP 有以下几种用例

  • 双重身份验证。 除了用户名和密码之外,SMS OTP 还可以作为强有力的信号,表明帐户所有者是收到 SMS OTP 的人。
  • 电话号码验证。 某些服务使用电话号码作为用户的主要标识符。在这些服务中,用户可以输入他们的电话号码以及通过 SMS 收到的 OTP,以证明自己的身份。有时,它会与 PIN 码结合使用,以构成双重身份验证。
  • 帐户恢复。 当用户无法访问其帐户时,需要有一种恢复方法。向其注册电子邮件地址发送电子邮件或向其电话号码发送 SMS OTP 是常见的帐户恢复方法。
  • 付款确认 在付款系统中,出于安全原因,一些银行或信用卡发卡机构会要求付款人进行额外的身份验证。SMS OTP 通常用于此目的。

这篇文章介绍了针对上述用例构建 SMS OTP 表单的最佳实践。

清单

为了提供最佳的 SMS OTP 用户体验,请按照以下步骤操作

  • 使用带有以下属性的 <input> 元素
    • type="text"
    • inputmode="numeric"
    • autocomplete="one-time-code"
  • 使用 @BOUND_DOMAIN #OTP_CODE 作为 OTP SMS 消息的最后一行。
  • 使用 WebOTP API

使用 <input> 元素

使用带有 <input> 元素的表单是您可以遵循的最重要的最佳实践,因为它适用于所有浏览器。即使这篇文章中的其他建议在某些浏览器中不起作用,用户仍然可以手动输入和提交 OTP。

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

以下是一些确保输入字段充分利用浏览器功能的想法。

type="text"

由于 OTP 通常是五位或六位数字,因此为输入字段使用 type="number" 似乎很直观,因为它会将移动键盘更改为仅数字键盘。但不建议这样做,因为浏览器希望输入字段是一个可计数的数字,而不是一系列数字,这可能会导致意外行为。使用 type="number" 会导致输入字段旁边显示向上和向下按钮;按下这些按钮会增加或减少数字,并可能删除前导零。

请改用 type="text"。这不会将移动键盘变成仅数字键盘,但这没关系,因为下一个关于使用 inputmode="numeric" 的技巧可以完成这项工作。

inputmode="numeric"

使用 inputmode="numeric" 将移动键盘更改为仅数字键盘。

某些网站对 OTP 输入字段使用 type="tel",因为它也会在聚焦时将移动键盘变为仅数字键盘(包括 *#)。当 inputmode="numeric" 未得到广泛支持时,过去曾使用过这种技巧。自从 Firefox 开始支持 inputmode="numeric" 以来,就没有必要使用语义不正确的 type="tel" 技巧了。

autocomplete="one-time-code"

autocomplete 属性允许开发人员指定浏览器提供自动完成辅助功能的权限,并告知浏览器该字段中预期的信息类型。

使用 autocomplete="one-time-code",每当用户在表单打开时收到 SMS 消息时,操作系统将启发式地解析 SMS 中的 OTP,并且键盘将建议 OTP 供用户输入。它仅适用于 iOS、iPadOS 和 macOS 上的 Safari 12 及更高版本,但我们强烈建议使用它,因为这是在这些平台上改善 SMS OTP 体验的一种简单方法。

`autocomplete="one-time-code"` 的实际应用。

autocomplete="one-time-code" 改善了用户体验,但您可以通过 确保 SMS 消息符合源绑定消息格式 来做更多事情。

格式化 SMS 文本

通过与 通过 SMS 传送的源绑定一次性代码 规范保持一致,从而增强输入 OTP 的用户体验。

格式规则很简单:在 SMS 消息的末尾添加接收者域名(以 @ 开头)和 OTP(以 # 开头)。

例如

Your OTP is 123456

@web-otp.glitch.me #123456

对 OTP 消息使用标准格式可以更轻松、更可靠地从中提取代码。将 OTP 代码与网站关联起来,使用户更难被诱骗向恶意网站提供代码。

使用这种格式有以下几个好处

  • OTP 将绑定到域名。如果用户位于 SMS 消息中指定的域名以外的域名上,则不会出现 OTP 建议。这也降低了网络钓鱼攻击和潜在帐户劫持的风险。
  • 浏览器现在能够可靠地提取 OTP,而无需依赖神秘且不可靠的启发式方法。

当网站使用 autocomplete="one-time-code" 时,带有 iOS 14 或更高版本的 Safari 将按照上述规则建议 OTP。

这种 SMS 消息格式也使 Safari 以外的浏览器受益。Android 上的 Chrome、Opera 和 Vivaldi 也通过 WebOTP API 支持源绑定一次性代码规则,但不是通过 autocomplete="one-time-code"

使用 WebOTP API

WebOTP API 提供了对 SMS 消息中收到的 OTP 的访问权限。通过使用 otp 类型 (OTPCredential) 调用 navigator.credentials.get(),其中 transport 包括 sms,网站将等待符合源绑定一次性代码的 SMS 消息传送,并由用户授予访问权限。一旦 OTP 传递给 JavaScript,网站就可以在表单中使用它,或者直接将其 POST 到服务器。

navigator.credentials.get({
  otp: {transport:['sms']}
})
.then(otp => input.value = otp.code);
WebOTP API 的实际应用。

要详细了解如何使用 WebOTP API,请参阅 使用 WebOTP API 验证 Web 上的电话号码,或复制并粘贴以下代码段。(确保 <form> 元素已正确设置 actionmethod 属性。)

// Feature detection
if ('OTPCredential' in window) {
  window.addEventListener('DOMContentLoaded', e => {
    const input = document.querySelector('input[autocomplete="one-time-code"]');
    if (!input) return;
    // Cancel the WebOTP API if the form is submitted manually.
    const ac = new AbortController();
    const form = input.closest('form');
    if (form) {
      form.addEventListener('submit', e => {
        // Cancel the WebOTP API.
        ac.abort();
      });
    }
    // Invoke the WebOTP API
    navigator.credentials.get({
      otp: { transport:['sms'] },
      signal: ac.signal
    }).then(otp => {
      input.value = otp.code;
      // Automatically submit the form when an OTP is obtained.
      if (form) form.submit();
    }).catch(err => {
      console.log(err);
    });
  });
}

照片由 Jason LeungUnsplash 上拍摄。