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

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

Eiji Kitamura
Eiji Kitamura

什麼是 WebOTP API?

時至今日,全世界大部分的人都有行動裝置,開發人員通常都會使用電話號碼做為服務使用者的 ID。

驗證電話號碼的方式有很多種,但透過簡訊傳送隨機產生的動態密碼 (OTP) 是最常見的做法之一。將此程式碼傳回開發人員伺服器,證明電話號碼的控制權。

目前已在許多情境中部署這個構想,以達成以下目標:

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

目前的程序為使用者帶來阻礙。從簡訊中找出動態密碼,然後複製並貼到表單中並不容易,關鍵使用者歷程中的轉換率可能會下降。簡單來說,這是長期以來對全球許多規模最大的開發人員提出的要求。Android 的 API 可以做到這點。但 iOSSafari 也是一樣。

WebOTP API 可讓應用程式接收繫結至應用程式網域的特殊格式訊息。這樣就能透過程式輔助方式從簡訊中取得動態密碼,以便為使用者輕鬆驗證電話號碼。

實例觀摩

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

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

整個程序如下圖所示。

WebOTP API 圖表

親自試試示範模式。系統不會要求提供電話號碼或傳送簡訊至您的裝置,但您可以複製示範中顯示的文字,藉此透過其他裝置傳送簡訊。之所以有效,是因為傳送者在使用 WebOTP API 時並不重要。

  1. 在 Chrome 第 84 版或以上版本中,前往 https://web-otp.glitch.me 的 Android 裝置。
  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> 標記中。

如此一來,Safari 14 以上版本可建議使用者在收到簡訊時,使用「設定簡訊格式」中所述格式,自動填入 <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');
    …
  });
}

處理動態密碼

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,嵌入程式必須透過 otp 憑證權限政策授予這個 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 時,請注意以下幾點:

  • 如果傳送者的電話號碼包含在接收者的聯絡人清單中,由於 SMS User Consent API 的設計,不會觸發這個 API。
  • 如果您在 Android 裝置上使用工作資料夾,但 WebOTP 無法正常運作,請嘗試改用個人資料夾安裝 Chrome (也就是接收簡訊的同一個設定檔)。

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

這個 API 是否與不同瀏覽器相容?

Chromium 和 WebKit 同意採用簡訊格式,且 Apple 自 iOS 14 和 macOS Big Sur 起宣布 Safari 支援這項功能。雖然 Safari 不支援 WebOTP JavaScript API,但使用 autocomplete=["one-time-code"]input 元素加上註解時,預設鍵盤會在簡訊符合格式的情況下自動建議您輸入動態密碼。

使用簡訊進行驗證是否安全?

雖然簡訊動態密碼可用於驗證首次提供電話號碼的情況,但電信業者可能會竊取及回收電話號碼,因此請務必謹慎地透過簡訊驗證電話號碼的方式重新驗證。WebOTP 是便利的重新驗證與復原機制,但服務應將其與額外因素 (例如知識驗證) 結合,或使用網路驗證 API 進行高強度驗證。

如何回報 Chrome 實作的錯誤?

您在導入 Chrome 時發現錯誤嗎?

  • 前往 https://new.crbug.com 回報錯誤。盡可能提供詳細資料、簡易的重新產生操作說明,並將「元件」設為 Blink>WebOTP

我要如何協助改善這項功能?

您打算使用 WebOTP API 嗎?您的公開支援團隊可以協助我們決定各項功能的優先順序,並讓其他瀏覽器供應商瞭解這些功能有多重要。使用主題標記 #WebOTP 傳送 Twitter 推文至 @ChromiumDev,並說明您使用了這項功能的位置和方式。

資源