# WebOTP API

> WebOTP 如何在用户同意后读取与域名绑定的短信验证码，包括短信格式、特性检测、取消请求与手动输入回退。

WebOTP API 扩展了 Credential Management，让网站请求与自身域名绑定的短信一次性验证码。用户同意后，`navigator.credentials.get()` 返回 `OTPCredential`，其中的 `code` 可填入验证表单（[WebOTP API](https://developer.mozilla.org/en-US/docs/Web/API/WebOTP_API)，MDN）。

## 支持范围（Support）

MDN 将 WebOTP 标为有限可用，并说明它只用于安全上下文。

其兼容性数据记录了桌面版 Chrome 93 和 Android 版 Chrome 84 起支持 `OTPCredential`，并将支持情况镜像至 Edge、Opera、Samsung Internet 与 Meta Quest Browser；Firefox、Safari 和 Android WebView 记为不支持（[WebOTP API](https://developer.mozilla.org/en-US/docs/Web/API/WebOTP_API)，MDN）。保留带 `autocomplete="one-time-code"` 的输入框可支持手动输入，也让 Safari 提供其独立的短信验证码自动填充（[Verify phone numbers on the web with the WebOTP API](https://developer.chrome.com/docs/identity/web-apis/web-otp)，Chrome for Developers）。

## 语法

```js
const credential = await navigator.credentials.get({
  otp: { transport: ['sms'] },
  signal,
});
```

短信末尾必须包含调用方域名和验证码。域名不含协议与端口，绑定域名的这一行必须位于短信末尾（[WebOTP API](https://developer.mozilla.org/en-US/docs/Web/API/WebOTP_API#sms_message_format)，MDN）。

## 参数

`otp` 选项的 `transport` 必须包含 `"sms"`，可选的 `AbortSignal` 可取消待处理请求；请求成功后，返回的 `OTPCredential` 用 `code` 给出收到的值（[WebOTP API](https://wicg.github.io/web-otp/)，WICG）。

## 异常

`otp-credentials` Permissions Policy 指令可禁用该 API。其默认允许列表为 `self`；策略拦截请求时，Promise 以 `SecurityError` 拒绝（[WebOTP API](https://developer.mozilla.org/en-US/docs/Web/API/WebOTP_API#controlling_access_to_the_api)，MDN）。中止信号触发时请求也可能拒绝，因此输入框和提交按钮仍应可用。

:::caution
Chrome 针对 Android 版 Chrome 84 及以上版本的说明显示：匹配短信到达后，底部面板带有 **Verify** 按钮。同一说明将短信最后一行写作 `@chrome.dev #123456`（[Verify phone numbers on the web with the WebOTP API](https://developer.chrome.com/docs/identity/web-apis/web-otp)，Chrome for Developers）。
:::

## 示例

首个示例展示服务端生成的短信格式；另一示例在请求验证码时保留手动验证路径。

### 编排与域名绑定的短信

以 `https://www.example.com` 为例，MDN 给出以下短信格式：

```text
Your verification code is 123456.

@www.example.com #123456
```

空行把说明文字与末尾绑定域名的一行分隔开。

### 自动填码并保留手动输入

无论 WebOTP 是否存在，此表单都保留标准输入框。用户手动提交会中止待处理的凭据请求。

```html
<form id="otp-form" action="/verify" method="post">
  <label>验证码 <input name="code" autocomplete="one-time-code" inputmode="numeric" required></label>
  <button>验证</button>
</form>
<script>
  const form = document.querySelector('#otp-form');
  const input = form.elements.code;

  if ('OTPCredential' in window) {
    const controller = new AbortController();
    form.addEventListener('submit', () => controller.abort(), { once: true });

    navigator.credentials.get({
      otp: { transport: ['sms'] },
      signal: controller.signal,
    }).then((otp) => {
      input.value = otp.code;
      form.requestSubmit();
    }).catch(() => {
      input.focus();
    });
  } else {
    input.focus();
  }
</script>
```

回退分支聚焦同一输入框，因此不支持该 API 的浏览器仍可走手动验证路径。

## 另请参阅

- [Credential Management API](/zh/reference/capabilities/credential-management/)
- [WebAuthn 与通行密钥](/zh/reference/capabilities/webauthn-passkeys/)
- [WebOTP API](https://wicg.github.io/web-otp/)（wicg.github.io）
- [WebOTP API](https://developer.mozilla.org/en-US/docs/Web/API/WebOTP_API)（MDN）