一幅一位女性使用 OTP 登录 Web 应用程序的图片。

使用 WebOTP API 验证网络上的电话号码

帮助通过短信收到 OTP 的用户

Published on Updated on

Translated to: English, Español, Português, 한국어, Pусский

重要的

如果您想了解更多一般短信 OTP 表单最佳实践,包括 WebOTP API,请查看短信 OTP 表单最佳实践

什么是 WebOTP API?

当今世界,大多数人都有一部移动设备,开发人员通常使用电话号码作为其服务用户的身份标识。

验证电话号码的方法有多种,但通过短信发送的随机生成的一次性密码 (OTP) 是最常见的方法之一。将此代码发回开发人员的服务器即表明对该电话号码拥有控制权。

这一想法已经部署在很多场景中来实现以下用途:

  • **电话号码作为用户的身份标识。**注册新服务时,某些网站会要求提供电话号码而不是电子邮件地址,并将其用作帐户标识。
  • **两步验证。**登录时,除密码或其他知识因素外,网站还会要求提供通过短信发送的一次性代码,以提高安全性。
  • **支付确认。**当用户进行付款时,要求提供通过短信发送的一次性代码可以帮助核实用户的意图。

目前的流程给用户带来了一些麻烦。在短信消息中找到 OTP,然后将其复制并粘贴到表单中非常烦琐,会降低关键用户旅程的转化率。解决这一问题一直是许多全球知名开发商对网络的长期要求。Android 有一个 API 可以做到这一点iOSSafari 也能做到。

WebOTP API 允许您的应用程序接收绑定到您应用程序域的特殊格式的消息。因此,您可以通过编程方式从短信消息中获取 OTP,并更轻松地为用户验证电话号码。

Warning

攻击者可以伪造短信并窃取他人的电话号码。运营商也可以在帐户注销后回收电话号码出售给新用户。虽然短信 OTP 可在上述用例中验证电话号码,但我们建议使用其他更强的身份验证形式(例如多重因素和 Web 身份验证 API)为这些用户建立新会话。

实现方式

假设某位用户想要在网站上验证自己的电话号码。该网站通过短信向用户发送短信,用户输入短信中的 OTP 来验证电话号码的所有权。

如视频中所示,使用 WebOTP API 后,只需用户轻轻一按即可实现这些步骤。收到短信时,手机底部会弹出一个表单提示用户验证电话号码。单击底部表单上的验证按钮后,浏览器会将 OTP 粘贴到表单中,用户无需按继续即可提交表单。

整个过程如下图所示。

WebOTP API 图

尝试一下演示中的操作。它不会询问您的电话号码或向您的设备发送短信,但您可以复制演示中显示的文本,从另一台设备发送短信。可以这样操作是因为在使用 WebOTP API 时发送方是谁并不重要。

  1. 在 Android 设备上的 Chrome 84 或更高版本中转到 https://web-otp.glitch.me
  2. 从另一部手机向您的手机发送以下短信。
Your OTP is: 123456.

@web-otp.glitch.me #12345

收到短信后看到提醒您在输入区输入代码的提示了吗?这就是 WebOTP API 为用户工作的方式。

重要的

如果该对话框没有出现,请查看常见问题解答

使用 WebOTP API 包括三个部分:

  • 正确注释的 <input> 标签
  • Web 应用程序中的 JavaScript
  • 通过短信发送的固定格式消息文本。

首先来介绍 <input> 标签。

<input> 标签加注释

WebOTP 本身无需任何 HTML 注释即可工作,但为了跨浏览器兼容性,强烈建议您将 autocomplete="one-time-code" 添加到您希望用户输入 OTP 的 <input> 标签中。

这样可以使 Safari 14 或更高版本建议用户在收到具有设置短信格式中所述格式的短信时,使用 OTP 自动填充 <input> 字段,即使它不支持 WebOTP。

HTML

<form>
<input autocomplete="one-time-code" required/>
<input type="submit">
</form>

使用 WebOTP API

WebOTP 非常简单,只需复制并粘贴下方的代码即可。不过,我对此过程进行说明。

JavaScript

if ('OTPCredential' in window) {
window.addEventListener('DOMContentLoaded', e => {
const input = document.querySelector('input[autocomplete="one-time-code"]');
if (!input) return;
const ac = new AbortController();
const form = input.closest('form');
if (form) {
form.addEventListener('submit', e => {
ac.abort();
});
}
navigator.credentials.get({
otp: { transport:['sms'] },
signal: ac.signal
}).then(otp => {
input.value = otp.code;
if (form) form.submit();
}).catch(err => {
console.log(err);
});
});
}

功能检测

功能检测与许多其他 API 相同。监听 DOMContentLoaded 事件将等待 DOM 树准备好后再开始查询。

JavaScript

if ('OTPCredential' in window) {
window.addEventListener('DOMContentLoaded', e => {
const input = document.querySelector('input[autocomplete="one-time-code"]');
if (!input) return;

const form = input.closest('form');

});
}
Caution

WebOTP API 需要安全来源 (HTTPS)。HTTP 网站上的功能检测将失败。

处理 OTP

WebOTP API 本身很简单。使用 navigator.credentials.get() 获取 OTP。WebOTP 向该方法添加了一个新的 otp 选项。它只有一个属性:transport ,其值必须是一个包含字符串 'sms' 的数组。

JavaScript


navigator.credentials.get({
otp: { transport:['sms'] }

}).then(otp => {

当 SMS 消息到达时,会触发浏览器的权限流。如果授予权限,则返回的承诺将使用 OTPCredential 对象进行解析。

获取的 `OTPCredential` 对象的内容

{
code: "123456" // 获取的 OTP
type: "otp" // `type` 始终为 "otp"
}

接下来,将 OTP 值传递给 <input> 字段。直接提交表单将省去需要用户点击按钮的步骤。

JavaScript


navigator.credentials.get({
otp: { transport:['sms'] }

}).then(otp => {
input.value = otp.code;
if (form) form.submit();
}).catch(err => {
console.error(err);
});

中止消息

如果用户手动输入 OTP 并提交表单,您可以在 options 对象中使用 AbortController 实例取消 get() 调用。

JavaScript


const ac = new AbortController();

if (form) {
form.addEventListener('submit', e => {
ac.abort();
});
}

navigator.credentials.get({
otp: { transport:['sms'] },
signal: ac.signal
}).then(otp => {

设置短信格式

API 本身看起来很简单,但在使用它之前,您需要了解一些注意事项。短信必须在调用 navigator.credentials.get() 之后发送,并且必须在调用 get() 的设备上接收。最后,短信必须遵循以下格式:

  • 短信以人类可读文本开头(可选),包含一个 4 到 10 个字符的字母数字字符串,其中至少有一个数字,最后一行用于 URL 和 OTP。
  • 调用 API 的网站 URL 的域部分必须以 @ 开头。
  • URL 必须包含一个井号 ('#'),后跟 OTP。

例如:

Your OTP is: 123456.

@www.example.com #123456

以下是错误示例:

格式错误的短信文本示例错误原因
Here is your code for @example.com #123456最后一行的第一个字符应为 @
Your code for @example.com is #123456最后一行的第一个字符应为 @
Your verification code is 123456

@example.com\t#123456
@host#code 之间应该有一个空格。
Your verification code is 123456

@example.com #123456
@host#code 之间应该有一个空格。
Your verification code is 123456

@ftp://example.com #123456
不能包含 URL 方案。
Your verification code is 123456

@https://example.com #123456
不能包含 URL 方案。
Your verification code is 123456

@example.com:8080 #123456
不能包含端口。
Your verification code is 123456

@example.com/foobar #123456
不能包含路径。
Your verification code is 123456

@example .com #123456
域中不能有空格。
Your verification code is 123456

@domain-forbiden-chars-#%/:<>?@[] #123456
域中不能有禁用字符
@example.com #123456

Mambo Jumbo
@host#code 应该是最后一行。
@example.com #123456

App hash #oudf08lkjsdf834
@host#code 应该是最后一行。
Your verification code is 123456

@example.com 123456
缺少 #
Your verification code is 123456

example.com #123456
缺少 @
Hi mom, did you receive my last text缺少 @#

演示

在演示中尝试各种消息:https://web-otp.glitch.me

您也可以拆分演示,创建您自己的版本:https://glitch.com/edit/#!/web-otp

使用跨域 iframe 中的 WebOTP

在跨域 iframe 中输入短信 OTP 通常用于支付确认,尤其是 3D Secure。WebOTP API 拥有支持跨域 iframe 的通用格式,提供绑定到嵌套源的 OTP。例如:

  • 用户访问 shop.example 使用信用卡购买一双鞋。
  • 输入信用卡号后,融合支付提供商会在 iframe 中显示一个来自 bank.example 的表单,要求用户验证其电话号码以便快速结账。
  • bank.example 向用户发送一条包含 OTP 的 短信,以便用户输入 OTP 来验证身份。

要在跨域 iframe 中使用 WebOTP API,您需要完成两项任务:

  • 为短信文本消息中的 top-frame 原点和 iframe 原点添加注释。
  • 配置权限策略,允许跨域 iframe 直接从用户处接收 OTP。
iframe 中的 WebOTP API 实例。

您可以尝试 https://web-otp-iframe-demo.stackblitz.io 中的演示。

为短信文本消息的 bound-origin 添加注释

从 iframe 内部调用 WebOTP API 时,短信文本消息必须包含以 @ 开头的 top-frame 原点后跟以 # 开头的 OTP,最后一行是以 @ 开头的 iframe 原点。

Your verification code is 123456

@shop.example #123456 @bank.exmple

配置权限策略

要在跨域 iframe 中使用 WebOTP,嵌入程序必须通过 otp-credentials 权限策略授予对该 API 的访问权限,以避免出现意外行为。一般来说,实现这一目标有两种方法:

通过 HTTP 标头:

Permissions-Policy: otp-credentials=(self "https://bank.example")

通过 iframe `allow` 属性:

<iframe src="https://bank.example/…" allow="otp-credentials"></iframe>

查看有关如何指定权限策略的更多示例

目前,Chrome 仅支持来自跨域 iframe 的 WebOTP API 调用,这些 iframe 在其祖先链中只有一个唯一来源。在以下场景中:

  • a.com -> b.com
  • a.com -> b.com -> b.com
  • a.com -> a.com -> b.com
  • a.com -> b.com -> c.com

支持在 b.com 中使用 WebOTP,但不支持在 c.com 中使用它。

请注意,由于缺乏需求和 UX 复杂性,也不支持以下场景。

  • a.com -> b.com -> a.com(调用 WebOTP API)

常见问题解答

我发送了格式正确的消息,但对话框没有出现。这是怎么回事?

测试 API 时有几个注意事项:

  • 如果发送方的电话号码在接收方的联系人列表中,则由于底层短信用户同意 API 的设计,不会触发此 API。
  • 如果您在 Android 设备上使用工作号码而 WebOTP 不起作用,请尝试在您的个人号码(即您接收短信的号码)上安装和使用 Chrome。

再次查阅格式,查看您的短信格式是否正确。

这个 API 在不同浏览器之间兼容吗?

Chromium 和 WebKit 就 短信文本消息格式达成一致, Apple 宣布 Safari 从 iOS 14 和 macOS Big Sur 开始支持该 API。尽管 Safari 不支持 WebOTP JavaScript API,但通过使用 autocomplete=["one-time-code"] 注释 input 元素,如果短信消息符合格式,默认键盘会自动建议您输入 OTP。

使用短信作为身份验证方式是否安全?

虽然短信 OTP 可用于在首次提供电话号码时验证电话号码,但通过短信进行电话号码验证以重新进行身份验证时必须谨慎,因为电话号码可能会被运营商窃取和回收。WebOTP 是一种方便的重新认证和恢复机制,但服务应该将它与其他因素结合起来,例如知识挑战,或者使用 Web 身份验证 API 进行强身份验证。

在哪里报告 Chrome 实现中的错误?

您是否发现了 Chrome 实现的错误?

  • https://new.crbug.com 中提交错误。尽可能包括更多的详细信息,再现错误的简单说明,并将组件设置为 Blink>WebOTP

如何帮助此功能?

您打算使用 WebOTP API 吗?您的公开支持可以帮助我们设置各项功能的优先级,并向其他浏览器供应商展示支持它们的重要性。使用主题标签 #WebOTP@ChromiumDev 发送推文,让我们知道您在何处以及如何使用它。

您可以在说明文档的常见问题解答部分找到更多问题。

Updated on 改进文章

This site uses cookies to deliver and enhance the quality of its services and to analyze traffic. If you agree, cookies are also used to serve advertising and to personalize the content and advertisements that you see. Learn more about our use of cookies.