パスワードなしでログインするためのパスキーを作成する

パスキーにより、ユーザー アカウントの安全性、シンプルさ、使いやすさが向上します。

パスワードの代わりにパスキーを使用すると、ウェブサイトでのユーザー アカウントの安全性、シンプルさ、使いやすさが向上し、パスワードが不要になります。パスキーを使用すると、ユーザーは指紋、顔認証、デバイスの PIN だけでウェブサイトやアプリにログインできます。

ユーザーがパスキーを使用してログインできるようにするには、ユーザー アカウントに関連付けられたパスキーを作成し、その公開鍵をサーバーに保存する必要があります。

仕組み

次のいずれかの状況で、ユーザーにパスキーの作成を求められることがあります。

  • ユーザーがパスワードを使用してログインした場合。
  • ユーザーが別のデバイスのパスキーを使用してログインした場合(つまり、authenticatorAttachmentcross-platform の場合)。
  • ユーザーがパスキーを管理できる専用ページ。

パスキーを作成するには、WebAuthn API を使用します。

パスキー登録フローには、次の 4 つのコンポーネントがあります。

  • バックエンド: 公開鍵とパスキーに関するその他のメタデータを格納するアカウント データベースを保持するバックエンド サーバー。
  • フロントエンド: ブラウザと通信し、バックエンドにフェッチ リクエストを送信します。
  • ブラウザ: JavaScript を実行しているユーザーのブラウザ。
  • Authenticator: パスキーを作成して保存するユーザーの認証システム。これは、ブラウザと同じデバイス(Windows Hello を使用している場合など)の場合もあれば、スマートフォンなどの別のデバイスにある場合もあります。
パスキー登録の図

既存のユーザー アカウントに新しいパスキーを追加する手順は次のとおりです。

  1. ユーザーがウェブサイトにログインします。
  2. ユーザーがログインしたら、[パスキーを作成] ボタンを押すなどして、フロントエンドでパスキーの作成をリクエストします。
  3. フロントエンドは、ユーザー情報、チャレンジ、除外する認証情報 ID などのパスキーを作成するために、バックエンドに情報をリクエストします。
  4. フロントエンドは navigator.credentials.create() を呼び出してパスキーを作成します。この呼び出しは Promise を返します。
  5. ユーザーがデバイスの画面ロックを使用して同意すると、パスキーが作成されます。Promise が解決され、公開鍵認証情報がフロントエンドに返されます。
  6. フロントエンドは公開鍵認証情報をバックエンドに送信し、今後の認証のために、認証情報 ID とユーザー アカウントに関連付けられた公開鍵を保存します。

互換性

WebAuthn はほとんどのブラウザでサポートされていますが、若干のギャップがあります。パスキーの作成をサポートしているブラウザとオペレーティング システムの組み合わせについては、デバイスのサポート - passkeys.dev をご覧ください。

新しいパスキーを作成する

新しいパスキーの作成リクエストに対してフロントエンドがどのように動作するかを次に示します。

機能検出

[新しいパスキーを作成] ボタンを表示する前に、次の点を確認してください。

  • ブラウザは WebAuthn をサポートしています。
  • デバイスがプラットフォーム認証システムをサポートしている(パスキーを作成して、そのパスキーで認証できます)。
  • ブラウザは WebAuthn 条件付き UI をサポートしています。
// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.  
// `​​isConditionalMediationAvailable` means the feature detection is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&  
    PublicKeyCredential.​​isConditionalMediationAvailable) {  
  // Check if user verifying platform authenticator is available.  
  Promise.all([  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),  
    PublicKeyCredential.​​isConditionalMediationAvailable(),  
  ]).then(results => {  
    if (results.every(r => r === true)) {  
      // Display "Create a new passkey" button  
    }  
  });  
}  

すべての条件が満たされるまで、このブラウザでパスキーはサポートされません。それまでは、[新しいパスキーを作成] ボタンは表示されません。

バックエンドから重要な情報を取得する

ユーザーがボタンをクリックしたときに、バックエンドから navigator.credentials.create() を呼び出すために重要な情報を取得します。

  • challenge: この登録のためにサーバーで生成された ArrayBuffer のチャレンジ。これは必須ですが、構成証明(ここでは取り上げていない高度なトピック)を行う場合を除き、登録時には使用されません。
  • user.id: ユーザーの一意の ID。この値は、メールアドレスやユーザー名などの個人を特定できる情報を含まない ArrayBuffer です。アカウントごとに生成されるランダムな 16 バイトの値で十分です。
  • user.name: このフィールドには、メールアドレスやユーザー名など、ユーザーが認識するアカウントの一意の識別子を格納します。この情報はアカウント選択ツールに表示されます。(ユーザー名を使用する場合は、パスワード認証と同じ値を使用します)。
  • user.displayName: このフィールドは必須で、よりわかりやすいアカウント名です。一意である必要はなく、ユーザーが選択した名前でもかまいません。ここに指定する適切な値がサイトにない場合は、空の文字列を渡してください。ブラウザによっては、アカウント選択ツールに表示される場合があります。
  • excludeCredentials: 登録済みの認証情報 ID のリストを提供することで、同じデバイスが登録されないようにします。transports メンバーを指定する場合は、各認証情報の登録時に getTransports() を呼び出した結果が含まれている必要があります。

WebAuthn API を呼び出してパスキーを作成する

navigator.credentials.create() を呼び出して、新しいパスキーを作成します。API は Promise を返し、モーダル ダイアログを表示するユーザーの操作を待機します。

const publicKeyCredentialCreationOptions = {
  challenge: *****,
  rp: {
    name: "Example",
    id: "example.com",
  },
  user: {
    id: *****,
    name: "john78",
    displayName: "John",
  },
  pubKeyCredParams: [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}],
  excludeCredentials: [{
    id: *****,
    type: 'public-key',
    transports: ['internal'],
  }],
  authenticatorSelection: {
    authenticatorAttachment: "platform",
    requireResidentKey: true,
  }
};

const credential = await navigator.credentials.create({
  publicKey: publicKeyCredentialCreationOptions
});

// Encode and send the credential to the server for verification.  

上記で説明されていないパラメータは次のとおりです。

  • rp.id: RP ID はドメインであり、ウェブサイトではドメインまたは登録可能なサフィックスのいずれかを指定できます。たとえば、RP の送信元が https://login.example.com:1337 の場合、RP ID は login.example.com または example.com のいずれかになります。RP ID が example.com として指定されている場合、ユーザーは login.example.com または example.com の任意のサブドメインで認証できます。

  • rp.name: RP の名前。

  • pubKeyCredParams: このフィールドには、RP でサポートされる公開鍵アルゴリズムを指定します。[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}] に設定することをおすすめします。これは、P-256 と RSA PKCS#1 による ECDSA のサポートを指定し、これらをサポートすることで、完全なカバレッジを提供します。

  • authenticatorSelection.authenticatorAttachment: このパスキーの作成がパスワードからのアップグレード(ログイン後のプロモーションなど)の場合は、これを "platform" に設定します。"platform" は、RP がプラットフォーム認証システム(プラットフォーム デバイスに埋め込まれた認証システム)を求めていることを示します。この認証システムは、USB セキュリティ キーなどの挿入を求めません。ユーザーは、パスキーを簡単に作成できます。

  • authenticatorSelection.requireResidentKey: ブール値「true」に設定します。検出可能な認証情報(レジデントキー)は、ユーザー情報をパスキーに保存し、認証時にユーザーがアカウントを選択できるようにします。

  • authenticatorSelection.userVerification: デバイスの画面ロックを使用したユーザー確認が "required""preferred""discouraged" のいずれであるかを示します。デフォルトは "preferred" です。この場合、認証システムはユーザー確認をスキップできます。"preferred" に設定するか、プロパティを省略します。

返された公開鍵認証情報をバックエンドに送信する

ユーザーがデバイスの画面ロックを使用して同意すると、パスキーが作成され、Promise が解決されて PublicKeyCredential オブジェクトがフロントエンドに返されます。

Promise はさまざまな理由で拒否されます。これらのエラーを処理するには、Error オブジェクトの name プロパティを確認します。

  • InvalidStateError: パスキーはすでにデバイスに存在します。エラー ダイアログはユーザーに表示されず、サイトではこれをエラーとして扱うべきではありません。ユーザーはローカル デバイスを登録したいと考えていましたが、それは事実です。
  • NotAllowedError: ユーザーがオペレーションをキャンセルしました。
  • その他の例外: 予期しないエラーが発生しました。ブラウザにエラー ダイアログが表示されます。

公開鍵認証情報オブジェクトには、次のプロパティが含まれています。

  • id: 作成されたパスキーの、Base64URL でエンコードされた ID。この ID により、ブラウザは認証時に一致するパスキーがデバイス内にあるかどうかを判断できます。この値は、バックエンドのデータベースに保存する必要があります。
  • rawId: 認証情報 ID の ArrayBuffer バージョン。
  • response.clientDataJSON: ArrayBuffer でエンコードされたクライアント データ。
  • response.attestationObject: ArrayBuffer でエンコードされた構成証明オブジェクト。これには、RP ID、フラグ、公開鍵などの重要な情報が含まれます。
  • authenticatorAttachment: パスキー対応デバイスでこの認証情報が作成されると、"platform" を返します。
  • type: このフィールドは常に "public-key" に設定されます。

ライブラリを使用して公開鍵認証情報オブジェクトをバックエンドで処理する場合は、オブジェクト全体を base64url で部分的にエンコードしてからバックエンドに送信することをおすすめします。

認証情報を保存する

バックエンドで公開鍵認証情報を受け取ったら、FIDO ライブラリに渡してオブジェクトを処理します。

後で使用するために、認証情報から取得した情報をデータベースに保存できます。以下に、保存する一般的なプロパティをいくつか示します。

  • 認証情報 ID(主キー)
  • ユーザー ID
  • 公開鍵

公開鍵認証情報には、データベースに保存する次の情報も含まれています。

ユーザーを認証するには、フォームの自動入力でパスキーを使用してログインするをご覧ください。

リソース