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

为了提供复杂的用户体验,请务必帮助用户在您的网站上进行身份验证。经过身份验证的用户可以使用专用配置文件相互互动、在设备间同步数据,或在离线状态下处理数据;此列表还会不断增加。但对于最终用户来说,创建、记住和输入密码往往非常麻烦,特别是在移动设备的屏幕上,这会导致他们在不同网站上重复使用相同的密码。这当然会带来安全风险。

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

什么是 Credential Management API?

借助 Credential Management 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,请参阅集成指南