認証情報管理 API の最新情報

ここで説明する更新の一部は、Google I/O セッション「安全でシームレスなログイン: ユーザーのエンゲージメントを維持する」で説明されています。

Chrome 57

Chrome 57 では、認証情報管理 API にこの重要な変更が導入されました。

認証情報を別のサブドメインから共有できる

Chrome は、Credential Management API を使用して、別のサブドメインに保存されている認証情報を取得できるようになりました。たとえば、パスワードが login.example.com に保存されている場合、www.example.com のスクリプトによって、アカウント選択ツールのダイアログでアカウント アイテムの 1 つとして表示できます。

ユーザーがダイアログをタップして認証情報を選択すると、パスワードが渡され、現在のオリジンにコピーされるように、navigator.credentials.store() を使用してパスワードを明示的に保存する必要があります。

保存されたパスワードは、まったく同じオリジン www.example.com 以降で認証情報として使用できます。

次のスクリーンショットでは、login.aliexpress.com に保存されている認証情報は m.aliexpress.com に表示され、ユーザーが選択できます。

選択したサブドメインのログイン情報を表示するアカウント選択ツール

Chrome 60

Chrome 60 では、認証情報管理 API にいくつかの重要な変更が導入されています。

機能検出に注意が必要

パスワードベースの認証情報と連携認証情報にアクセスするための Credential Management API が使用可能かどうかを確認するには、window.PasswordCredential または window.FederatedCredential が使用可能かどうかを確認します。

if (window.PasswordCredential || window.FederatedCredential) {
  // The Credential Management API is available
}

PasswordCredential オブジェクトにパスワードが含まれるようになりました

認証情報マネージメント API は、パスワードの処理に保守的なアプローチを採用していました。JavaScript からパスワードを隠蔽するため、デベロッパーは fetch() API の拡張機能を使用して、PasswordCredential オブジェクトをサーバーに直接送信して検証する必要がありました。

ただし、このアプローチにはいくつかの制限がありました。デベロッパーから、次のような理由で API を使用できないというフィードバックが寄せられました。

  • パスワードを JSON オブジェクトの一部として送信する必要がありました。

  • パスワードのハッシュ値をサーバーに送信する必要がありました。

セキュリティ分析を実施した結果、JavaScript からパスワードを隠しても、期待したほど効果的にすべての攻撃ベクトルを防ぐことができなかったため、変更を決定しました。

Credential Management API では、返された認証情報オブジェクトに未加工のパスワードが含まれるようになりました。これにより、プレーンテキストとしてパスワードにアクセスできるようになりました。既存の方法を使用して、認証情報の情報をサーバーに提供できます。

navigator.credentials.get({
    password: true,
    federated: {
    providers: [ 'https://accounts.google.com' ]
    },
    mediation: 'silent'
}).then(passwordCred => {
    if (passwordCred) {
    let form = new FormData();
    form.append('email', passwordCred.id);
    form.append('password', passwordCred.password);
    form.append('csrf_token', csrf_token);
    return fetch('/signin', {
        method: 'POST',
        credentials: 'include',
        body: form
    });
    } else {

    // Fallback to sign-in form
    }
}).then(res => {
    if (res.status === 200) {
    return res.json();
    } else {
    throw 'Auth failed';
    }
}).then(profile => {
    console.log('Auth succeeded', profile);
});

カスタム取得はまもなく非推奨になります

カスタム fetch() 関数を使用しているかどうかを確認するには、credentials プロパティの値として PasswordCredential オブジェクトと FederatedCredential オブジェクトのどちらが使用されているかを確認します。次に例を示します。

fetch('/signin', {
    method: 'POST',
    credentials: c
})

前のコード例に示すように、通常の fetch() 関数を使用するか、XMLHttpRequest を使用することをおすすめします。

Chrome 60 までは、navigator.credentials.get() はブール値フラグを持つ省略可能な unmediated プロパティを受け入れていました。次に例を示します。

navigator.credentials.get({
    password: true,
    federated: {
    providers: [ 'https://accounts.google.com' ]
    },
    unmediated: true
}).then(c => {

    // Sign-in
});

unmediated: true を設定すると、認証情報を渡すときにブラウザでアカウント選択画面が表示されなくなります。

このフラグはメディエーションとして拡張されました。 ユーザー メディエーションは、次のような場合に発生する可能性があります。

  • ログインに使用するアカウントを選択する必要があります。

  • ユーザーが navigator.credentials.requireUseMediation() 呼び出し後に明示的にログインすることを希望している。

mediation 値に次のいずれかのオプションを選択します。

mediation ブール値フラグとの比較 動作
silent unmediated: true に等しい アカウント選択画面を表示せずに認証情報が渡された。
optional unmediated: false に等しい preventSilentAccess() が以前に呼び出された場合は、アカウント選択ツールが表示されます。
required 新しいオプション 常にアカウント選択ツールを表示します。 ネイティブのアカウント選択ツールのダイアログを使用してユーザーにアカウントを切り替えさせる場合に便利です。

この例では、アカウント選択ツールを表示せずに認証情報が渡されます。これは、前のフラグ unmediated: true と同等です。

navigator.credentials.get({
    password: true,
    federated: {
    providers: [ 'https://accounts.google.com' ]
    },
    mediation: 'silent'
}).then(c => {

    // Sign-in
});

requireUserMediation() の名前を preventSilentAccess() に変更しました

get() 呼び出しで提供される新しい mediation プロパティと整合させるため、navigator.credentials.requireUserMediation() メソッドの名前が navigator.credentials.preventSilentAccess() に変更されました。

名前が変更されたメソッドでは、アカウント選択ツールを表示せずに認証情報を渡すことを防ぎます(ユーザー介入なしとも呼ばれます)。これは、ユーザーがウェブサイトからログアウトまたは登録解除し、次回アクセスしたときに自動的にログインしないようにする場合に便利です。

signoutUser();
if (navigator.credentials) {
    navigator.credentials.preventSilentAccess();
}

新しいメソッド navigator.credentials.create() を使用して認証情報オブジェクトを非同期で作成

新しいメソッド navigator.credentials.create() を使用して、認証情報オブジェクトを非同期で作成できるようになりました。同期と非同期の両方のアプローチの比較については、以下をご覧ください。

PasswordCredential オブジェクトの作成

同期方法
let c = new PasswordCredential(form);
非同期アプローチ(新規)
let c = await navigator.credentials.create({
    password: form
});

または

let c = await navigator.credentials.create({
    password: {
    id: id,
    password: password
    }
});

FederatedCredential オブジェクトの作成

同期方法
let c = new FederatedCredential({
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
});
非同期アプローチ(新規)
let c = await navigator.credentials.create({
    federated: {
    id:       'agektmr',
    name:     'Eiji Kitamura',
    provider: 'https://accounts.google.com',
    iconURL:  'https://*****'
    }
});

移行ガイド

Credential Management API の既存の実装はありますか?新しいバージョンにアップグレードする際は、移行ガイドのドキュメントを参照してください。