使用憑證管理 API 簡化登入流程

如要提供精緻的使用者體驗,請務必協助使用者驗證網站。已驗證的使用者可以使用專屬設定檔彼此互動、在不同裝置間同步處理資料,或在離線時處理資料,這類情況不勝枚舉。不過,對於使用者來說,建立、記住和輸入密碼往往相當麻煩,尤其是在行動裝置螢幕上,因此使用者會在不同網站重複使用相同的密碼。這當然會帶來安全風險。

最新版的 Chrome (51) 支援 Credential Management API。這是 W3C 的標準追蹤提案,可讓開發人員以程式輔助方式存取瀏覽器的憑證管理工具,並協助使用者更輕鬆地登入。

什麼是憑證管理 API?

憑證管理 API 可讓開發人員儲存及擷取密碼憑證和聯合憑證,並提供 3 項功能:

  • navigator.credentials.get()
  • navigator.credentials.store()
  • navigator.credentials.requireUserMediation()

開發人員可以透過這些簡單的 API 執行強大的功能,例如:

  • 讓使用者只要輕觸一下就能登入。
  • 記住使用者用來登入的聯合帳戶。
  • 在工作階段到期時,讓使用者重新登入。

在 Chrome 的實作中,憑證會儲存在 Chrome 的密碼管理工具中。如果使用者已登入 Chrome,就能在各裝置間同步處理使用者的密碼。這些已同步的密碼也可以與已整合 Android 版 Smart Lock for Passwords API 的 Android 應用程式共用,以提供順暢的跨平台體驗

將憑證管理 API 整合至網站

您在網站上使用憑證管理 API 的方式,可能會因架構而異。這是單頁應用程式嗎?是否為具有頁面轉場的舊版架構?登入表單是否只位於頂端頁面?是否在各處顯示登入按鈕?使用者可以在不登入的情況下,瀏覽您的網站嗎?聯盟是否可在彈出式視窗中運作?還是需要跨多個網頁進行互動?

我們幾乎無法涵蓋所有情況,但讓我們來看看典型的單頁應用程式。

  • 頂端頁面是註冊表單。
  • 輕觸「登入」按鈕後,使用者會前往登入表單。
  • 註冊和登入表單都提供 ID/密碼憑證和聯合登入的常見選項,例如 Google 登入和 Facebook 登入。

使用憑證管理 API 後,您就能在網站上新增下列功能,例如:

  • 登入時顯示帳戶選擇器:在使用者輕觸「登入」時顯示原生帳戶選擇器 UI。
  • 儲存憑證:登入成功後,提供將憑證資訊儲存至瀏覽器密碼管理工具的選項,以供日後使用。
  • 讓使用者自動重新登入:如果工作階段已過期,請讓使用者重新登入。
  • 中介自動登入:使用者登出後,系統會在使用者下次造訪時停用自動登入功能。

您可以使用試用網站程式碼範例,體驗這些功能。

在登入時顯示帳戶選擇器

在使用者輕觸「Sign In」按鈕和前往登入表單之間,您可以使用 navigator.credentials.get() 取得憑證資訊。Chrome 會顯示帳戶選擇器 UI,讓使用者選擇帳戶。

系統會彈出帳戶選擇器 UI,供使用者選取要登入的帳戶。
系統會彈出帳戶選擇器 UI,讓使用者選取要登入的帳戶

取得密碼憑證物件

如要將密碼憑證顯示為帳戶選項,請使用 password: true

navigator.credentials.get({
    password: true, // `true` to obtain password credentials
}).then(function(cred) {
    // continuation
    ...

使用密碼憑證登入

使用者選取帳戶後,解析函式就會收到密碼憑證。您可以使用 fetch() 將其傳送至伺服器:

    // continued from previous example
}).then(function(cred) {
    if (cred) {
    if (cred.type == 'password') {
        // Construct FormData object
        var form = new FormData();

        // Append CSRF Token
        var csrf_token = document.querySelector('csrf_token').value;
        form.append('csrf_token', csrf_token);

        // You can append additional credential data to `.additionalData`
        cred.additionalData = form;

        // `POST` the credential object as `credentials`.
        // id, password and the additional data will be encoded and
        // sent to the url as the HTTP body.
        fetch(url, {           // Make sure the URL is HTTPS
        method: 'POST',      // Use POST
        credentials: cred    // Add the password credential object
        }).then(function() {
        // continuation
        });
    } else if (cred.type == 'federated') {
        // continuation

使用聯合憑證登入

如要向使用者顯示聯合帳戶,請將 federated 新增至 get() 選項,這會採用一系列的 ID 供應器。

密碼管理工具中儲存多個帳戶時。
密碼管理工具中儲存多個帳戶時
navigator.credentials.get({
    password: true, // `true` to obtain password credentials
    federated: {
    providers: [  // Specify an array of IdP strings
        'https://accounts.google.com',
        'https://www.facebook.com'
    ]
    }
}).then(function(cred) {
    // continuation
    ...

您可以檢查憑證物件的 type 屬性,瞭解該屬性是否為 PasswordCredential (type == 'password') 或 FederatedCredential (type == 'federated')。如果憑證為 FederatedCredential,您可以使用其中包含的資訊呼叫適當的 API。

    });
} else if (cred.type == 'federated') {
    // `provider` contains the identity provider string
    switch (cred.provider) {
    case 'https://accounts.google.com':
        // Federated login using Google Sign-In
        var auth2 = gapi.auth2.getAuthInstance();

        // In Google Sign-In library, you can specify an account.
        // Attempt to sign in with by using `login_hint`.
        return auth2.signIn({
        login_hint: cred.id || ''
        }).then(function(profile) {
        // continuation
        });
        break;

    case 'https://www.facebook.com':
        // Federated login using Facebook Login
        // continuation
        break;

    default:
        // show form
        break;
    }
}
// if the credential is `undefined`
} else {
// show form
憑證管理流程圖。

儲存憑證

當使用者使用表單登入網站時,您可以使用 navigator.credentials.store() 儲存憑證。系統會提示使用者是否要儲存。視憑證類型而定,請使用 new PasswordCredential()new FederatedCredential() 建立要儲存的憑證物件。

Chrome 會詢問使用者是否要儲存憑證 (或聯合服務供應器)。
Chrome 會詢問使用者是否要儲存憑證 (或聯合服務供應器)

透過表單元素建立及儲存密碼憑證

以下程式碼會使用 autocomplete 屬性,自動將表單元素對應PasswordCredential 物件參數。

HTML html <form id="form" method="post"> <input type="text" name="id" autocomplete="username" /> <input type="password" name="password" autocomplete="current-password" /> <input type="hidden" name="csrf_token" value="******" /> </form>

JavaScript

var form = document.querySelector('\#form');
var cred = new PasswordCredential(form);
// Store it
navigator.credentials.store(cred)
.then(function() {
    // continuation
});

建立及儲存聯合式憑證

// After a federation, create a FederatedCredential object using
// information you have obtained
var cred = new FederatedCredential({
    id: id,                                  // The id for the user
    name: name,                              // Optional user name
    provider: 'https://accounts.google.com',  // A string that represents the identity provider
    iconURL: iconUrl                         // Optional user avatar image url
});
// Store it
navigator.credentials.store(cred)
.then(function() {
    // continuation
});
登入流程圖。

讓使用者自動重新登入

如果使用者離開網站後又回訪,該工作階段可能會過期。不要讓使用者每次返回時都輸入密碼。讓使用者自動登入。

使用者自動登入時,系統會彈出通知。
當使用者自動登入時,系統會彈出通知。

取得憑證物件

navigator.credentials.get({
    password: true, // Obtain password credentials or not
    federated: {    // Obtain federation credentials or not
    providers: [  // Specify an array of IdP strings
        'https://accounts.google.com',
        'https://www.facebook.com'
    ]
    },
    unmediated: true // `unmediated: true` lets the user automatically sign in
}).then(function(cred) {
    if (cred) {
    // auto sign-in possible
    ...
    } else {
    // auto sign-in not possible
    ...
    }
});

程式碼應類似於「登入時顯示帳戶選擇器」一節中所示。唯一的差別在於您會設定 unmediated: true

這會立即解析函式,並提供憑證,讓您自動登入使用者。但有幾個條件:

  • 使用者在歡迎畫面中確認自動登入功能。
  • 使用者先前曾使用憑證管理 API 登入網站。
  • 使用者只儲存一個來源憑證。
  • 使用者未在先前的工作階段中明確登出。

如果不符合上述任一條件,系統就會拒絕該函式。

憑證物件流程圖

自動登入中介服務

當使用者從您的網站登出時,您有責任確保使用者不會自動重新登入。為確保這項功能,憑證管理 API 提供稱為「中介」的機制。您可以呼叫 navigator.credentials.requireUserMediation() 來啟用中介模式。只要使用者針對來源啟用中介服務狀態,使用 unmediated: true 搭配 navigator.credentials.get(),該函式就會透過 undefined 解析。

自動登入仲介

navigator.credentials.requireUserMediation();
自動登入流程圖。

常見問題

網站上的 JavaScript 是否可以擷取原始密碼?否。您只能透過 PasswordCredential 取得密碼,而且密碼無法透過任何方式揭露。

是否可以使用憑證管理 API 為 ID 儲存 3 組數字?目前不行。我們非常感謝您提供規格相關意見回饋

我可以在 iframe 中使用憑證管理 API 嗎?這個 API 僅限於頂層情境。在 iframe 中對 .get().store() 的呼叫會立即解析,但不會產生效果。

可以將密碼管理 Chrome 擴充功能與 Credential Management API 整合嗎?您可以覆寫 navigator.credentials,並將其鉤掛至 Chrome 擴充功能,以便使用 get()store() 憑證。

資源

如要進一步瞭解 Credential Management API,請參閱整合指南