高度なユーザー エクスペリエンスを提供するには、ユーザーがウェブサイトで本人確認を行うことをサポートすることが重要です。認証されたユーザーは、専用のプロファイルを使用して相互にやり取りしたり、デバイス間でデータを同期したり、オフラインでデータを処理したりできます。ただし、パスワードの作成、記憶、入力は、特にモバイル画面ではエンドユーザーにとって煩雑になりがちで、異なるサイトで同じパスワードを再利用する結果につながります。これはもちろんセキュリティ リスクです。
最新バージョンの Chrome(51)は 認証情報管理 API をサポートしています。これは W3C の標準トラック プロポーザルで、デベロッパーがブラウザの認証情報マネージャーにプログラムでアクセスできるようにし、ユーザーが簡単にログインできるようにします。
認証情報マネージャー 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 を使用すると、次のような機能をサイトに追加できます。
- ログイン時にアカウント選択ツールを表示: ユーザーが [ログイン] をタップすると、ネイティブのアカウント選択ツール UI が表示されます。
- 認証情報を保存する: ログインが成功すると、後で使用できるように認証情報をブラウザのパスワード マネージャーに保存することを提案します。
- ユーザーが自動的に再ログインできるようにする: セッションが期限切れになった場合に、ユーザーが再ログインできるようにします。
- 自動ログインを仲介する: ユーザーがログアウトすると、ユーザーの次回のアクセスで自動ログインを無効にします。
これらの機能は、デモサイトで実装されており、サンプルコードで確認できます。
ログイン時にアカウント選択ツールを表示する
ユーザーが [ログイン] ボタンをタップしてからログイン フォームに移動するまでの間、navigator.credentials.get() を使用して認証情報を取得できます。Chrome にアカウント選択 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
連携認証情報を使用してログインする
連携アカウントをユーザーに表示するには、get() オプションに federated を追加します。これは、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() を使用して、保存する認証情報オブジェクトを作成します。
フォーム要素からパスワード認証情報を作成して保存する
次のコードでは、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 を使用してウェブサイトにログインしている。
- ユーザーがオリジンの認証情報を 1 つだけ保存している。
- ユーザーが前のセッションで明示的にログアウトしていない。
これらの条件のいずれかが満たされていない場合、関数は拒否されます。
自動ログインを仲介する
ユーザーがウェブサイトからログアウトしたときに、ユーザーが自動的にログインしないようにするのはお客様の責任です。これを実現するために、Credential Management API にはメディエーションと呼ばれるメカニズムが用意されています。メディエーション モードを有効にするには、navigator.credentials.requireUserMediation() を呼び出します。オリジンのユーザーのメディエーション ステータスがオンになっている限り、unmediated: true と navigator.credentials.get() を使用して、その関数は undefined で解決されます。
自動ログインの仲介
navigator.credentials.requireUserMediation();
よくある質問
ウェブサイトの JavaScript が未加工のパスワードを取得することは可能ですか?いいえ。パスワードは PasswordCredential の一部としてのみ取得でき、いかなる方法でも公開できません。
認証情報管理 API を使用して、ID の 3 桁のセットを保存できますか?現時点では受け付けていません。仕様に関するフィードバックをお寄せください。
iframe 内で Credential Management API を使用できますか?この API は最上位のコンテキストに制限されています。iframe での .get() または .store() の呼び出しは、効果なしで直ちに解決されます。
パスワード管理の Chrome 拡張機能を認証情報管理 API と統合できますか?navigator.credentials をオーバーライドして、Chrome 拡張機能にフックし、get() または store() 認証情報に接続できます。
リソース
認証情報管理 API の詳細については、統合ガイドをご覧ください。