使用凭据管理 API 简化登录流程

为了提供完善的用户体验,请务必帮助用户对您的网站进行身份验证。经过身份验证的用户可以使用专用个人资料相互互动、跨设备同步数据,或在离线状态下处理数据;诸如此类的用例不胜枚举。但是,对于最终用户来说,创建、记住和输入密码通常很麻烦,尤其是在移动屏幕上,这会导致他们在不同的网站上重复使用相同的密码。这当然会带来安全风险。

最新版本的 Chrome(51)支持 Credential Management API。这是 W3C 的标准轨道提案,可让开发者以编程方式访问浏览器的凭据管理器,并帮助用户更轻松地登录。

什么是 Credential Management API?

借助 Credential Management API,开发者可以存储和检索密码凭据和联合凭据,该 API 提供 3 项功能:

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

通过使用这些简单的 API,开发者可以执行强大的操作,例如:

  • 让用户只需点按一下即可登录。
  • 记住用户用于登录的联合账号。
  • 在会话过期时让用户重新登录。

在 Chrome 的实现中,凭据将存储在 Chrome 的密码管理工具中。如果用户已登录 Chrome,则可以跨设备同步用户的密码。这些已同步的密码还可与集成了 Android 版 Smart Lock for Passwords API 的 Android 应用共享,从而获得流畅的跨平台体验

将 Credential Management API 与您的网站集成

您在网站上使用 Credential Management API 的方式可能会因其架构而异。它是单页应用吗?它是否采用了包含页面转换的旧版架构?登录表单是否仅位于顶部页面?登录按钮是否随处可见?用户能否在不登录的情况下浏览您的网站?联合是否适用于弹出式窗口?还是需要跨多个页面进行互动?

我们几乎不可能涵盖所有这些情况,但我们来看看典型的单页应用。

  • 顶部页面是注册表单。
  • 用户点按“登录”按钮后,会进入登录表单。
  • 注册表单和登录表单都提供 ID/密码凭据和联合登录(例如使用 Google 账号登录和 Facebook 账号登录)等常见选项。

通过使用 Credential Management API,您将能够向网站添加以下功能,例如:

  • 在登录时显示账号选择器:在用户点按“登录”时显示原生账号选择器界面。
  • 存储凭据:在用户成功登录后,提供将凭据信息存储到浏览器密码管理工具中以供日后使用的选项。
  • 允许用户自动重新登录:如果会话已过期,允许用户重新登录。
  • 中介自动登录:在用户退出账号后,停用用户下次访问时的自动登录功能。

您可以通过演示网站及其示例代码体验这些功能的实现方式。

在登录时显示账号选择器

在用户点按“登录”按钮和转到登录表单之间,您可以使用 navigator.credentials.get() 获取凭据信息。Chrome 会显示一个账号选择器界面,用户可以在其中选择账号。

系统会弹出账号选择器界面,供用户选择要登录的账号。
系统弹出账号选择器界面,供用户选择要登录的账号

获取密码凭据对象

如需将密码凭据显示为账号选项,请使用 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() 选项。

在密码管理工具中存储多个账号时。
当密码管理工具中存储多个账号时
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

这会立即解析该函数,并为您提供用于自动登录用户的凭据。需要满足以下条件:

  • 用户已在热烈欢迎界面中确认自动登录功能。
  • 用户之前使用 Credential Management API 登录过该网站。
  • 用户只有一个为您的来源存储的凭据。
  • 用户在之前的会话中未明确退出账号。

如果不满足上述任一条件,该函数将被拒绝。

凭据对象流程图

中介自动登录

当用户从您的网站退出时,您有责任确保用户不会自动重新登录。为此,Credential Management API 提供了一种称为中介的机制。您可以通过调用 navigator.credentials.requireUserMediation() 来启用中介模式。只要用户的来源中介状态处于开启状态,将 unmediated: truenavigator.credentials.get() 搭配使用时,该函数将使用 undefined 解析。

中介自动登录

navigator.credentials.requireUserMediation();
自动登录流程图。

常见问题解答

网站上的 JavaScript 是否可以检索原始密码? 不会。您只能在 PasswordCredential 中获取密码,并且密码不会以任何方式泄露。

能否使用 Credential Management API 为 ID 存储 3 组数字? 目前不支持。我们非常期待您就规范提供反馈,不胜感激。

我可以在 iframe 中使用 Credential Management API 吗? 此 API 仅限于顶级上下文。对 iframe 中的 .get().store() 的调用将立即解析,但不会产生任何影响。

我可以将密码管理 Chrome 扩展程序与 Credential Management API 集成吗? 您可以替换 navigator.credentials,并将其钩接到 Chrome 扩展程序以获取 get()store() 凭据。

资源

如需详细了解 Credential Management API,请参阅集成指南