WebOTP API
WebOTP API 提供了一種簡化的使用者體驗,讓 Web 應用程式可以在使用者使用手機號碼作為登入因素時,驗證手機號碼是否屬於使用者。WebOTP 是 Credential Management API 的一個擴充套件。
驗證透過一個兩步過程完成。
- 應用客戶端請求一個一次性密碼(OTP),該 OTP 來自應用伺服器傳送的一個特殊格式的簡訊。
- 使用 JavaScript 將 OTP 輸入到應用客戶端的驗證表單中,然後將其提交回伺服器以驗證是否與簡訊中原始傳送的內容匹配。
概念與用法
電話號碼通常被用作識別應用使用者的標識。簡訊經常被用來驗證該號碼是否屬於使用者。簡訊通常包含一個 OTP,使用者需要複製並貼上到應用中的表單中,以驗證他們擁有該號碼。這是一種相當笨拙的使用者體驗。
OTP 的使用場景包括:
- 透過使用電話號碼作為 多因素身份驗證 系統的一部分來提高登入安全性。
- 驗證敏感操作,例如付款。
WebOTP API 允許 Web 應用程式透過自動複製簡訊中的 OTP 並將其傳遞給應用程式來加快此驗證過程,前提是使用者已提供同意(大多數原生平臺都有等效的 API)。
請注意,OTP 繫結到傳送域。這是一個有用的安全限制,用於驗證 OTP 是否來自正確的來源,這可以減輕日常重新驗證期間網路釣魚攻擊的風險。
簡訊 OTP 的安全顧慮
簡訊 OTP 對於驗證電話號碼很有用,並且使用簡訊作為第二個因素肯定比沒有第二個因素要好。在某些地區,電子郵件地址和身份驗證器等其他識別符號並未得到廣泛使用,因此簡訊 OTP 非常普遍。
然而,簡訊的安全性不高。攻擊者可以偽造簡訊並劫持一個人的電話號碼。運營商可以在賬戶關閉後將電話號碼回收給新使用者。
因此,建議您如果可能,使用更強的身份驗證形式,例如基於 Web Authentication API 的解決方案,包括密碼和安全金鑰或通行金鑰。
WebOTP API 如何工作?
過程如下:
- 在需要電話號碼驗證的節點,應用客戶端會要求使用者在表單中輸入他們的電話號碼,然後將其提交給應用伺服器。
- 應用客戶端然後呼叫
navigator.credentials.get(),並附帶一個otp選項,指定transport型別為"sms"。這會觸發從底層系統請求 OTP,其來源是一個從應用伺服器接收到的特殊格式的簡訊(包含 OTP 和應用的域名)。get()呼叫是基於Promise的,並等待接收簡訊。 - 應用伺服器將簡訊傳送到指定的電話號碼。這必須在步驟 2 發生後立即進行。
- 當裝置收到簡訊時,如果其中包含應用的域名,瀏覽器會詢問使用者是否同意檢索/使用 OTP。例如,Chrome 會顯示一個對話方塊,請求使用者允許從簡訊中檢索 OTP;其他瀏覽器可能處理方式不同。如果使用者同意,
get()呼叫將以一個包含 OTP 的OTPCredential物件返回。 - 然後,您可以按需使用 OTP。典型用法是將其設定為應用客戶端驗證表單的值,然後提交表單,使過程儘可能無縫。
- 應用伺服器然後驗證回傳給它的 OTP 是否與它最初在簡訊中傳送的一致,如果一致,則完成過程(例如,登入使用者)。
簡訊格式
典型的簡訊看起來像這樣:
Your verification code is 123456. @www.example.com #123456
- 第一行和第二行空白行是可選的,用於人類可讀性。
- 最後一行是強制性的。如果存在其他行,它必須是最後一行,並且必須由以下部分組成:
- 呼叫 API 的網站 URL 的域名部分,前面加上一個
@。 - 後面跟著一個空格。
- 後面跟著 OTP,前面加上一個井號(
#)。
- 呼叫 API 的網站 URL 的域名部分,前面加上一個
注意:提供的域名值不得包含 URL 方案、埠或其他未在上方顯示的 URL 要素。
如果 get() 方法由嵌入在 <iframe> 中的第三方網站呼叫,則簡訊結構應為:
Your verification code is 123456. @top-level.example.com #123456 @embedded.com
在這種情況下,最後一行必須由以下部分組成:
- 頂級域的域名部分,前面加上一個
@。 - 後面跟著一個空格。
- 後面跟著 OTP,前面加上一個井號(
#)。 - 後面跟著一個空格。
- 後面跟著嵌入域的域名部分,前面加上一個
@。
控制對 API 的訪問
WebOTP 的可用性可以使用 Permissions Policy 進行控制,該策略指定了 otp-credentials 指令。此指令的預設允許列表值為 "self",這意味著預設情況下,這些方法可以在頂層文件上下文中用於。
您可以指定一個指令,允許在特定的跨域域中使用 WebOTP(即,在 <iframe> 中),如下所示:
Permissions-Policy: otp-credentials=(self "https://embedded.com")
或者您也可以直接在 <iframe> 上指定,如下所示:
<iframe src="https://embedded.com/..." allow="otp-credentials"> ... </iframe>
注意:在策略禁止使用 WebOTP get() 的地方,它返回的 Promise 將會因 SecurityError DOMException 而拒絕。
介面
OTPCredential-
WebOTP
get()呼叫成功時返回;包含一個code屬性,其中包含檢索到的 OTP。
其他介面的擴充套件
CredentialsContainer.get(),otp選項-
使用
otp選項呼叫get()會指示使用者代理嘗試從底層系統的簡訊應用中檢索 OTP。
示例
在此示例中,當收到簡訊並用戶授予許可權後,會返回一個帶有 OTP 的 OTPCredential 物件。然後,此密碼會預填充到驗證表單欄位中,並提交表單。
表單欄位包含一個值為 one-time-code 的 autocomplete 屬性。這對於 WebOTP API 的工作不是必需的,但值得包含。因此,即使 WebOTP API 在 Safari 中不受完全支援,當收到格式正確的簡訊時,Safari 也會提示使用者使用 OTP 自動填充此欄位。
<input type="text" autocomplete="one-time-code" inputmode="numeric" />
JavaScript 如下:
// Detect feature support via OTPCredential availability
if ("OTPCredential" in window) {
const input = document.querySelector('input[autocomplete="one-time-code"]');
if (!input) return;
// Set up an AbortController to use with the OTP request
const ac = new AbortController();
const form = input.closest("form");
if (form) {
// Abort the OTP request if the user attempts to submit the form manually
form.addEventListener("submit", (e) => {
ac.abort();
});
}
// Request the OTP via get()
navigator.credentials
.get({
otp: { transport: ["sms"] },
signal: ac.signal,
})
.then((otp) => {
// When the OTP is received by the app client, enter it into the form
// input and submit the form automatically
input.value = otp.code;
if (form) form.submit();
})
.catch((err) => {
console.error(err);
});
}
另一個使用 AbortController 的好方法是,在一段時間後取消 get() 請求。
setTimeout(() => {
// abort after 30 seconds
ac.abort();
}, 30 * 1000);
如果使用者分心或導航到其他地方,取消請求很重要,這樣他們就不會看到不再相關的許可權提示。
規範
| 規範 |
|---|
| WebOTP API |
瀏覽器相容性
載入中…
另見
- 來自 developer.chrome.com (2023) 的 使用 WebOTP 在 Web 上驗證電話號碼
- 使用 WebOTP API 在跨域 iframe 中填充 OTP 表單