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

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

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

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

仕組み

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

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

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

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

  • バックエンド: 公開鍵とパスキーに関するその他のメタデータを格納するアカウント データベースを保持するバックエンド サーバー。
  • フロントエンド: ブラウザと通信し、バックエンドにフェッチ リクエストを送信するフロントエンドです。
  • ブラウザ: JavaScript を実行しているユーザーのブラウザ。
  • 認証システム: パスキーを作成して保存するユーザーの認証システム。ブラウザと同じデバイス(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
  • 公開鍵

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

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

リソース