パスワードレスのログイン用のパスキーを作成する

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

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

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

仕組み

パスキーの作成を求められる状況は次のとおりです。

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

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

パスキー登録フローの 4 つのコンポーネントは次のとおりです。

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

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

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

互換性

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

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

ここでは、新しいパスキーの作成がリクエストされたときに、フロントエンドがどのように動作するかを示します。

特徴検出

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

  • ブラウザが PublicKeyCredential で WebAuthn をサポートしている。

対応ブラウザ

  • Chrome: 67。
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13。

ソース

  • デバイスは PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() でプラットフォーム認証器をサポートしています(パスキーを作成してパスキーで認証できます)。

対応ブラウザ

  • Chrome: 67。
  • Edge: 18.
  • Firefox: 60。
  • Safari: 13。

ソース

  • ブラウザが PublicKeyCredenital.isConditionalMediationAvailable()WebAuthn 条件付き UI をサポートしている。

対応ブラウザ

  • Chrome: 108。
  • Edge: 108.
  • Firefox: 119。
  • Safari: 16。

ソース

// 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 はプロミスを返します。このプロミスは、ユーザー操作を待機してモーダル ダイアログを表示します。

対応ブラウザ

  • Chrome: 60。
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13。

ソース

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 の ECDSA と RSA PKCS#1 がサポートされていることを意味します。これらをサポートすることで、全範囲をカバーできます。

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

  • authenticatorSelection.requireResidentKey: ブール値「true」に設定します。検出可能な認証情報(常駐キー)には、ユーザー情報がパスキーに保存され、ユーザーは認証時にアカウントを選択できます。検出可能な認証情報の詳細については、検出可能な認証情報の詳細をご覧ください。

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

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

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

プロミスはさまざまな理由で拒否されることがあります。これらのエラーは、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(主キー)
  • User-ID
  • 公開鍵

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

詳しい手順については、サーバーサイドのパスキー登録をご覧ください。

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

リソース