Signal API を使用して、パスキーとサーバー上の認証情報の整合性を保つ

公開日: 2024 年 11 月 12 日

WebAuthn Signal API を使用すると、証明書利用者は接続されたパスキー プロバイダに既存の認証情報を通知できます。これにより、対応するパスキー プロバイダは、誤ったパスキーや取り消されたパスキーをストレージから更新または削除し、ユーザーに提供されないようにすることができます。

互換性

パソコン版 Chrome では、Chrome 132 以降で Signal API がサポートされています。Google パスワード マネージャーは、シグナルを反映してパスキーを更新できます。Chrome 拡張機能ベースのパスキープ プロバイダの場合、シグナルを反映するかどうかはプロバイダ次第です。

Android 版 Chrome のサポートは今後提供される予定です。

Safari はサポートしていますが、まだ実装されていません。Firefox はまだ意見を発表していません

背景

パスキー(検出可能な認証情報)が作成されると、ユーザー名や表示名などのメタデータが、秘密鍵とともにパスキー プロバイダ(パスワード マネージャーなど)に保存されます。一方、公開鍵認証情報は、信頼する当事者(RP)のサーバーに保存されます。ユーザー名と表示名を保存すると、プロンプトが表示されたときに、どのパスキーでログインするかをユーザーが識別しやすくなります。これは、異なるパスキー プロバイダから 2 つ以上のパスキーがある場合に特に便利です。

ただし、パスキー プロバイダのパスキー リストとサーバーの認証情報リストの不一致が混乱を招く場合があります。

最初のケースは、ユーザーがサーバー上の認証情報を削除し、パスキー プロバイダのパスキーには変更を加えない場合です。次回ユーザーがパスキーでログインしようとすると、そのパスキーはパスキー プロバイダからユーザーに提示されます。ただし、削除された公開鍵でサーバーが検証できないため、ログインは失敗します。

2 つ目のケースは、ユーザーがサーバー上でユーザー名または表示名を更新した場合です。ユーザーが次回ログインしようとすると、サーバー上で更新されたにもかかわらず、パスキー プロバイダのパスキーには古いユーザー名と表示名が引き続き表示されます。理想的には、両者が同期している必要があります。

Signal API

Signal API は WebAuthn API であり、RP がパスキー プロバイダに変更を通知できるようにすることで、こうした混乱を解消します。次の 3 つの方法があります。

認証情報が存在しないことを通知する

const credential = await navigator.credentials.get({ ... });
const payload = credential.toJSON();

const result = await fetch('/login', { ... });

// Detect authentication failure due to lack of the credential
if (result.status === 404) {
  // Feature detection
  if (PublicKeyCredential.signalUnknownCredential) {
    await PublicKeyCredential.signalUnknownCredential({
      rpId: "example.com",
      credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
    });
  } else {
    // Encourage the user to delete the passkey from the password manager nevertheless.
    ...
  }
}

RP ID と認証情報 ID を指定して PublicKeyCredential.signalUnknownCredential() を呼び出すと、RP は指定された認証情報が削除されたか存在しないことをパスキー プロバイダに通知できます。このシグナルをどのように処理するかはパスキー プロバイダ次第ですが、関連付けられた認証情報が存在しないため、ユーザーがパスキーでログインしないように、関連付けられたパスキーが削除されることが想定されます。

この API は、認証情報がないためパスキーベースのログインが失敗した場合に呼び出されます。これにより、RP は、関連する認証情報のないパスキーでユーザーがログインしようとするのを防ぐことができます。signalAllAcceptedCredentials とは異なり、このメソッドでは認証情報 ID のリストをすべて渡す必要がないため、ユーザーが認証されていない場合は常に使用し、特定のユーザーのパスキーの数が漏洩しないようにする必要があります。

Chrome の Google パスワード マネージャーからパスキーが削除されたときに表示されるダイアログ。
Chrome の Google パスワード マネージャーからパスキーが削除されたときに表示されるダイアログ。

保存された認証情報のリストを通知する

// After a user deletes a passkey or a user is signed in.

// Feature detection
if (PublicKeyCredential.signalAllAcceptedCredentials) {
  await PublicKeyCredential.signalAllAcceptedCredentials({
    rpId: "example.com",
    userId: "M2YPl-KGnA8", // base64url encoded user ID
    allAcceptedCredentialIds: [ // A list of base64url encoded credential IDs
      "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA",
      ...
    ]
  });
}

RP ID、ユーザー ID、保存されている認証情報の認証情報 ID のリストを指定して PublicKeyCredential.signalAllAcceptedCredentials() を呼び出すと、RP はストレージに残っている認証情報をパスキー プロバイダに通知できます。このシグナルへの対応方法はパスキー プロバイダに委ねられますが、このリストと一致しないパスキーは削除されるため、関連する認証情報が存在しないパスキーがログイン時にユーザーに表示されなくなります。

この API は、RP でユーザーがパスキーを削除したときログインするたびに呼び出す必要があります。これにより、パスキー プロバイダはパスキーの同期リストを保持できます。

ユーザー名と表示名の更新を通知する

// After a user updated their username and/or display name
// or a user is signed in.

// Feature detection
if (PublicKeyCredential.signalCurrentUserDetails) {
  await PublicKeyCredential.signalCurrentUserDetails({
    rpId: "example.com",
    userId: "M2YPl-KGnA8", // base64url encoded user ID
    name: "a.new.email.address@example.com", // username
    displayName: "J. Doe"
  });
} else {
}

RP ID、ユーザー ID、ユーザー名、表示名を指定して PublicKeyCredential.signalCurrentUserDetails() を呼び出すと、RP は更新されたユーザー情報をパスキー プロバイダに通知できます。このシグナルへの対応方法はパスキー プロバイダに委ねられますが、ユーザーが所有するパスキーは新しいユーザー情報で更新されることが期待されます。

この API は、ユーザーのユーザー名または表示名が更新されたときログインするたびに呼び出すことができ、パスキー プロバイダはこの情報をサーバーと同期できます。

Chrome の Google パスワード マネージャーでパスキーのメタデータが更新されたときに表示されるダイアログ。
Chrome の Google パスワード マネージャーでパスキーのメタデータが更新されたときに表示されるダイアログ。

概要

Signal API を使用すると、予期しないログイン失敗の可能性を排除し、より優れたパスキー エクスペリエンスを構築できます。Signal API を使用すると、信頼できる当事者は既存の認証情報とそのメタデータのリストを通知できるため、パスキー プロバイダのパスキーを同期した状態に保つことができます。

パスキーの詳細については、パスキーによるパスワードなしのログインをご覧ください。