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

パスキーを使用すると、ユーザー アカウントの安全性、シンプルさ、使いやすさを向上させることができます。

パスワードの代わりにパスキーを使用すると、ウェブサイトでのユーザー アカウントの安全性、シンプルさ、使いやすさを向上させることができます。パスキーを使用すると、ユーザーは指紋、顔認証、デバイスの 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" は、USB セキュリティ キーなどの挿入を要求しないプラットフォーム認証システム(プラットフォーム デバイスに埋め込まれた認証システム)を RP が希望していることを示します。ユーザーがパスキーを作成するための簡単な方法がある。

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

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

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

ユーザーがデバイスの画面ロックの使用に同意すると、パスキーが作成され、プロミスが解決して 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
  • 公開鍵

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

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

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

リソース