認証情報管理 API を使用したログインフローの効率化

Eiji Kitamura
Eiji Kitamura

高度なユーザー エクスペリエンスを提供するには、ウェブサイトでユーザーが自身を認証できるようにすることが重要です。認証されたユーザーは、専用のプロファイルを使用して相互にやり取りしたり、デバイス間でデータを同期したり、オフライン中にデータを処理したりできます。しかし、パスワードの作成、記憶、入力は、エンドユーザーにとって面倒な作業になりがちです。特にモバイル画面では、複数のサイトで同じパスワードを使い回してしまいます。これは当然ながら セキュリティ上のリスクです

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 にログイン済みのユーザーは、デバイス間でユーザーのパスワードを同期できます。これらの同期されたパスワードは、Smart Lock for Passwords API for Android を統合した Android アプリでも共有できるため、シームレスなクロス プラットフォーム エクスペリエンスを実現できます。

Credential Management API とサイトの統合

ウェブサイトで Credential Management API を使用する方法は、アーキテクチャによって異なる場合があります。シングルページ アプリですか。ページ遷移のあるレガシー アーキテクチャですか?ログイン フォームがトップページにのみ配置されているか?ログインボタンはいたるところにありますか?ユーザーがログインせずに有意義に ウェブサイトを閲覧できるかポップアップ ウィンドウ内で連携は機能しますか?それとも複数のページをまたいで操作する必要があるか?

すべてのケースを網羅することはほぼ不可能ですが、一般的な単一ページアプリを見てみましょう。

  • トップページは登録フォームです。
  • ユーザーが [ログイン] ボタンをタップすると、ログイン フォームに移動します。
  • 登録フォームとログイン フォームのどちらにも、ID/パスワードの認証情報や連携(Google ログインや Facebook ログインなど)の一般的なオプションがあります。

Credential Management API を使用すると、次のような機能をサイトに追加できます。

  • ログイン時に Account Chooser を表示する: ユーザーが [ログイン] をタップしたときに、ネイティブのアカウント選択ツールの UI を表示します。
  • 認証情報を保存する: ログインに成功したら、後で使用するために、認証情報をブラウザのパスワード マネージャーに保存することを提案します。
  • ユーザーが自動的に再ログインできるようにする: セッションが期限切れになった場合、ユーザーが再ログインできるようにします。
  • 自動ログインをメディエーションする: ユーザーがログアウトした後、次回アクセスする際に自動ログインを無効にします。

これらの機能は、サンプルコードを使用してデモサイトでお試しいただけます。

ログイン時に Account Chooser を表示する

ユーザーが [ログイン] ボタンをタップしてからログイン フォームに移動するまでの間に、navigator.credentials.get() を使用して認証情報を取得できます。Chrome でアカウント選択ツールの UI が表示され、ユーザーはそこからアカウントを選択できます。

Account Chooser 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

連携認証情報を使用したログイン

連携アカウントをユーザーに表示するには、ID プロバイダの配列を受け取る federatedget() オプションに追加します。

複数のアカウントがパスワード マネージャーに保存されている場合。
パスワード マネージャーに複数のアカウントが登録されている場合
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 プロパティを調べて、それが PasswordCredentialtype == 'password')と FederatedCredentialtype == '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 が、認証情報(またはフェデレーション プロバイダ)を保存するかどうかをユーザーに尋ねる

フォーム要素からパスワード認証情報を作成して保存する

次のコードは、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
    ...
    }
});

コードは、「ログイン時に Account Chooser を表示する」で説明したようなものです。唯一の違いは unmediated: true を設定する点です。

これにより、関数が直ちに解決され、ユーザーを自動的にログインさせるための認証情報が提供されます。いくつかの条件があります。

  • ユーザーがウォーム ウェルカムで自動ログイン機能を確認しました。
  • ユーザーは以前に Credential Management API を使用してウェブサイトにログインしています。
  • ユーザーがオリジンに対して保存されている認証情報は 1 つのみです。
  • ユーザーが前回のセッションで明示的にログアウトしていなかった。

これらの条件のいずれかが満たされない場合、関数は拒否されます。

認証情報オブジェクトのフロー図

自動ログインをメディエーションする

ユーザーがウェブサイトからログアウトした場合、そのユーザーが自動的に再ログインしないように、デベロッパー様の責任でご対応ください。これを実現するために、Credential Management API にはメディエーションというメカニズムが用意されています。メディエーション モードを有効にするには、navigator.credentials.requireUserMediation() を呼び出します。navigator.credentials.get()unmediated: true を使用して、オリジンに対するユーザーのメディエーション ステータスがオンになっている限り、その関数は 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 について詳しくは、統合ガイドをご覧ください。