使用 WebOTP API 驗證網路上的電話號碼

協助使用者透過簡訊接收動態密碼

什麼是 WebOTP API?

現在大多數的人都有行動裝置,而開發人員經常會使用電話號碼做為其服務使用者的 ID。

驗證電話號碼的方法有很多種,但簡訊傳送的隨機密碼 (OTP) 是最常見的一種。將這組代碼傳回開發人員的伺服器,可證明電話號碼的控制權。

這個想法已部署在許多情境中,以便達成:

  • 電話號碼做為使用者的 ID。註冊新服務時,有些網站會要求提供電話號碼 (而不是電子郵件地址),並將這組號碼做為帳戶 ID。
  • 兩步驟驗證。登入時,網站會詢問在密碼或其他知識因素之外,還會透過簡訊傳送一次性驗證碼,藉此提供額外的安全保障。
  • 確認付款。當使用者付款時,要求透過簡訊傳送一次性代碼,即可驗證對方的意圖。

目前的程序會對使用者造成困擾。在簡訊中尋找動態密碼,然後將其複製並貼到表單中非常麻煩,降低重要使用者歷程的轉換率。這向來是全球最大的網路開發商長久以來對網路的要求。Android 的API 可滿足這項需求iOSSafari 也是如此。

WebOTP API 可讓應用程式接收與應用程式網域繫結的特殊格式訊息。這樣一來,您就可以透過程式取得簡訊中的動態密碼,並更輕鬆地為使用者驗證電話號碼。

實例觀摩

假設某位使用者想透過網站驗證電話號碼。網站會透過簡訊向使用者發送簡訊,使用者輸入簡訊中的動態密碼,藉此驗證電話號碼的擁有權。

有了 WebOTP API,使用者只需輕觸一下就能輕鬆完成這些步驟,如影片所示。收到簡訊時,系統會彈出底部功能表,並提示使用者驗證電話號碼。按一下底部功能表的「Verify」按鈕後,瀏覽器會將動態密碼貼到表單中,使用者無需按下「Continue」即可提交表單。

整個流程如下圖所示。

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> 標記
  • 網頁應用程式中的 JavaScript
  • 已透過簡訊傳送格式化訊息文字。

我會先遮蓋 <input> 標記。

<input> 標記加上註解

WebOTP 本身無需任何 HTML 註解,但為了支援跨瀏覽器的相容性,強烈建議您將 autocomplete="one-time-code" 新增至預期使用者輸入動態密碼的 <input> 標記中。

這樣一來,即使不支援 WebOTP,Safari 14 以上版本仍建議使用者在收到以「設定簡訊格式」中所述格式的簡訊時,自動填入 <input> 欄位和動態密碼。

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');
    …
  });
}

處理動態密碼

WebOTP API 本身就夠簡單。使用 navigator.credentials.get() 取得動態密碼。WebOTP 會為該方法新增 otp 選項。它只有一個屬性:transport,其值必須是包含 'sms' 字串的陣列。

JavaScript

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

這樣會在收到簡訊時觸發瀏覽器的權限流程。授予權限後,傳回的承諾項目會由 OTPCredential 物件解析。

已取得 OTPCredential 物件的內容

{
  code: "123456" // Obtained OTP
  type: "otp"  // `type` is always "otp"
}

接著,將動態密碼值傳遞至 <input> 欄位。直接提交表單後,使用者就不需要輕觸按鈕。

JavaScript

    …
    navigator.credentials.get({
      otp: { transport:['sms'] }
      …
    }).then(otp => {
      input.value = otp.code;
      if (form) form.submit();
    }).catch(err => {
      console.error(err);
    });
    …

正在取消訊息

如果使用者手動輸入動態密碼並提交表單,您可以使用 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 個英數字元的字串,且在最後一行中至少要填入一個數字 (網址和動態密碼)。
  • 叫用 API 的網站網址的網域部分必須在 @ 前面。
  • 網址必須包含井字號 (「#」) 後接動態密碼。

例如:

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
不可加入網址配置。
Your verification code is 123456

@https://example.com #123456
不可加入網址配置。
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 通常用於確認付款,特別是使用 3D Secure 時。WebOTP API 採用支援跨來源 iframe 的通用格式,可傳送繫結至巢狀來源的 OTP。舉例來說:

  • 使用者造訪 shop.example 並用信用卡購買了一雙鞋,
  • 輸入信用卡號後,整合的付款服務供應商會在 iframe 中顯示 bank.example 的表單,要求使用者驗證電話號碼以快速結帳。
  • bank.example 會向使用者傳送含有動態密碼的簡訊,以便使用者輸入這組動態密碼並驗證身分。

如要從跨來源 iframe 中使用 WebOTP API,您必須執行以下兩項操作:

  • 在簡訊中,同時為頂端頁框來源和 iframe 來源加上註解。
  • 設定權限政策,允許跨來源 iframe 直接接收來自使用者的動態密碼。
iframe 中的 WebOTP API 實際運作情形。

您可以在 https://web-otp-iframe-demo.stackblitz.io 中查看示範。

為簡訊簡訊的繫結來源加上註解

如果從 iframe 內呼叫 WebOTP API,簡訊必須包括頂端頁框來源 (前面加上 @,後面接上 # 的 OTP),以及在最後一行後面加上 @ 的 iframe 來源。

Your verification code is 123456

@shop.example #123456 @bank.exmple

設定權限政策

如要在跨來源 iframe 中使用 WebOTP,嵌入程式必須透過虛擬憑證權限政策授予這個 API 的存取權,以避免非預期的行為。一般來說,達成這個目標的方法有兩種:

透過 HTTP 標頭:

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

透過 iframe allow 屬性:

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

請參閱更多如何指定權限政策的範例

在電腦上使用 WebOTP

在 Chrome 中,WebOTP 支援監聽其他裝置的簡訊,協助使用者在電腦上完成電話號碼驗證。

電腦版 WebOTP API。

使用者必須在電腦版 Chrome 和 Android 版 Chrome 上登入同一個 Google 帳戶,才能使用這項功能。

所有開發人員都必須在電腦版網站上實作 WebOTP API,方法與在行動版網站上實作相同,但不需要使用特殊技巧。

詳情請參閱在電腦上使用 WebOTP API 驗證電話號碼

常見問題

我傳送的郵件格式正確,但系統並未顯示對話方塊。發生了什麼事?

測試 API 時,請注意以下幾點:

  • 如果傳送者的電話號碼在接收者的聯絡人清單中,這個 API 就不會因為基礎 SMS User Consent API 的設計而觸發。
  • 如果您在 Android 裝置上使用工作資料夾,且 WebOTP 無法運作,請嘗試改為在個人設定檔安裝並使用 Chrome (也就是用來接收簡訊的設定檔)。

請檢查格式,查看您的簡訊格式是否正確。

這個 API 是否適用於不同的瀏覽器?

Chromium 和 WebKit 也同意簡訊格式,且 Apple 自 iOS 14 和 macOS Big Sur 宣布支援 Safari 支援機制。雖然 Safari 不支援 WebOTP JavaScript API,但預設鍵盤會使用 autocomplete=["one-time-code"]input 元素加上註解,但是如果簡訊符合格式,鍵盤會自動建議您輸入動態密碼。

使用簡訊進行驗證安全嗎?

雖然簡訊動態密碼在初次提供電話號碼時相當實用,不過為了重新驗證電話號碼,請務必謹慎使用透過簡訊進行驗證,因為電信業者可能會把電話號碼綁架及回收。WebOTP 是便利的重新驗證和復原機制,但服務應將其與額外因素 (例如知識驗證) 搭配使用,或使用 Web Authentication API 執行高強度驗證作業。

該到哪裡回報 Chrome 實作環境錯誤?

您在執行 Chrome 時發現錯誤了嗎?

  • 請前往 https://new.crbug.com 回報錯誤。請盡可能提供詳細資料、簡易的重現操作說明,以及將「Components」設為 Blink>WebOTP

我要如何解決這個問題?

您打算使用 WebOTP API 嗎?您的公開支援可協助我們決定各項功能的優先順序,以及向其他瀏覽器廠商瞭解這項功能有多重要。請使用主題標記 #WebOTP 將 Tweet 訊息傳送至 @ChromiumDev,並告訴我們您的使用地點和方式。

資源