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

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

公開日: 2022 年 10 月 12 日、最終更新日: 2026 年 4 月 9 日

パスキーを使用すると、セキュリティが強化され、ログインが簡素化され、パスワードが置き換えられます。ユーザーが覚えて手動で入力する必要がある通常のパスワードとは異なり、パスキーは生体認証や PIN などのデバイスの画面ロック メカニズムを使用するため、フィッシングのリスクや認証情報の盗難を軽減できます。

パスキーは、Google パスワード マネージャーや iCloud キーチェーンなどのパスキー プロバイダを使用してデバイス間で同期されます。

パスキーを作成し、必要なメタデータとともに秘密鍵をパスキー プロバイダに安全に保存し、認証用の公開鍵をサーバーに保存する必要があります。秘密鍵は、有効なドメインでユーザー確認を行った後に署名を発行するため、パスキーはフィッシングに強いものになります。公開鍵は機密性の高い認証情報を保存せずに署名を検証するため、パスキーは認証情報の盗難に強いです。

パスキーの作成の仕組み

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

ユーザーにパスキーの作成を求めるのは、次のような場合です。

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

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

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

  • バックエンド: 公開鍵を含むユーザー アカウントの詳細を保存します。
  • フロントエンド: ブラウザと通信し、バックエンドから必要なデータを取得します。
  • ブラウザ: JavaScript を実行し、WebAuthn API とやり取りします。
  • パスキー プロバイダ: パスキーを作成して保存します。通常、Google パスワード マネージャーなどのパスワード マネージャー、またはセキュリティ キーです。
パスキーを作成して登録するプロセス
パスキーの作成と登録のプロセス。

パスキーを作成する前に、システムが次の前提条件を満たしていることを確認してください。

  • ユーザー アカウントは、意味のある短い期間内に安全な方法(メール、電話認証、ID 連携など)で確認されます。

  • フロントエンドとバックエンドは安全に通信して、認証情報データを交換できます。

  • ブラウザが WebAuthn とパスキーの作成をサポートしている。

以降のセクションでは、ほとんどの項目を確認する方法について説明します。

システムがこの条件を満たすと、次のプロセスが実行されてパスキーが作成されます。

  1. ユーザーがアクションを開始すると(たとえば、パスキー管理ページで [パスキーを作成] ボタンをクリックしたり、登録を完了したりすると)、システムはパスキー作成プロセスをトリガーします。
  2. フロントエンドは、重複を防ぐために、ユーザー情報、チャレンジ、認証情報 ID などの必要な認証情報データをバックエンドにリクエストします。
  3. フロントエンドは navigator.credentials.create() を呼び出して、デバイスのパスキー プロバイダにバックエンドからの情報を使用してパスキーを生成するよう促します。この呼び出しは Promise を返します。
  4. ユーザーのデバイスは、生体認証、PIN、またはパターンを使用してユーザーを認証し、パスキーを作成します。
  5. パスキー プロバイダはパスキーを作成し、公開鍵認証情報をフロントエンドに返して、Promise を解決します。
  6. フロントエンドは、生成された公開鍵認証情報をバックエンドに送信します。
  7. バックエンドは、今後の認証で使用する公開鍵やその他の重要なデータを保存します。
  8. バックエンドは、パスキーの作成を確認し、不正なアクセスを検出するために、ユーザーに通知します(メールなどを使用)。

このプロセスにより、ユーザーは安全かつシームレスにパスキーを登録できます。

互換性

ほとんどのブラウザは WebAuthn をサポートしていますが、いくつかの小さなギャップがあります。ブラウザと OS の互換性の詳細については、passkeys.dev をご覧ください。

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

新しいパスキーを作成する際に、フロントエンドが従うべきプロセスは次のとおりです。

  1. 互換性を確認します。
  2. バックエンドから情報を取得します。
  3. WebAuth API を呼び出してパスキーを作成します。
  4. 返された公開鍵をバックエンドに送信します。
  5. 認証情報を保存します。

以降のセクションでは、その方法について説明します。

互換性の確認

[新しいパスキーを作成] ボタンを表示する前に、フロントエンドは次のことを確認する必要があります。

  • ブラウザは PublicKeyCredential で WebAuthn をサポートしています。

Browser Support

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

Source

  • ブラウザは PublicKeyCredential.getClientCapabilities() による機能検出をサポートしています。

Browser Support

  • Chrome: 133.
  • Edge: 133.
  • Firefox: 135.
  • Safari: 17.4.

Source

  • ブラウザが conditionalGetWebAuthn 条件付き UI をサポートしている。

  • デバイスは passkeyPlatformAuthenticator を使用してプラットフォーム認証システム(デバイスでパスキーを作成して認証できる)をサポートしています。

次のコード スニペットは、パスキー関連のオプションを表示する前に互換性を確認する方法を示しています。

if (window.PublicKeyCredential && PublicKeyCredential.getClientCapabilities) {
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  if (capabilities.conditionalGet === true &&
      capabilities.passkeyPlatformAuthenticator === true) {
    // The browser supports passkeys and the conditional UI.
  }
}

この例では、すべての条件が満たされている場合にのみ [新しいパスキーを作成] ボタンが表示されます。

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

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

次のコード スニペットは、navigator.credentials.create() を呼び出すために必要な情報を含む JSON オブジェクトを示しています。

// Example `PublicKeyCredentialCreationOptions` contents
{
  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,
  }
}

オブジェクト内の Key-Value ペアには、次の情報が保持されます。

  • challenge: この登録用にサーバーで生成された ArrayBuffer のチャレンジ。
  • rp.id: RP ID(Relying Party ID)、ドメイン、ウェブサイトは、ドメインまたは登録可能なサフィックスを指定できます。たとえば、RP のオリジンが https://login.example.com:1337 の場合は、RP ID として login.example.comexample.com のいずれかを指定できます。RP ID が example.com として指定されている場合、ユーザーは login.example.com で認証を行うことができます。また、example.com の任意のサブドメインでも認証を行うことができます。詳しくは、関連オリジン リクエストを使用してサイト間でパスキーを再利用できるようにするをご覧ください。
  • rp.name: RP(証明書利用者)の名前。これは WebAuthn L3 では非推奨ですが、互換性の理由から含まれています。
  • user.id: アカウントの作成時に生成される ArrayBuffer 内の一意のユーザー ID。編集可能なユーザー名とは異なり、永続的なものである必要があります。ユーザー ID はアカウントを識別しますが、個人を特定できる情報(PII)は含めないでください。システムにユーザー ID がすでにある可能性が高いですが、必要に応じて、パスキー専用のユーザー ID を作成して、PII が含まれないようにします。
  • user.name: ユーザーが認識できるアカウントの一意の識別子(メールアドレスやユーザー名など)。この識別子はアカウント選択画面に表示されます。
  • user.displayName: アカウントのわかりやすい名前(必須)。一意である必要はなく、ユーザーが選択した名前でも構いません。このフィールドに含めるのに適した値がサイトにない場合は、空の文字列を渡してください。ブラウザによっては、アカウント選択画面にこの値が表示されることがあります。
  • pubKeyCredParams: RP(証明書利用者)でサポートされる公開鍵アルゴリズムを指定します。[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}] に設定することをおすすめします。この設定は、P-256 の ECDSA と RSA PKCS#1 がサポートされていることを意味します。これらをサポートすることで、全範囲をカバーできます。
  • excludeCredentials: 登録済みの認証情報 ID のリスト。すでに登録されている認証情報 ID のリストを指定することで、同じデバイスが 2 回登録されないようにしますtransports メンバーが指定されている場合、メンバーには、各認証情報の登録時に getTransports() 関数を呼び出した結果が含まれている必要があります。
  • authenticatorSelection.authenticatorAttachment: このパスキーの作成が、ログイン後のプロモーションなどでのパスワードからのアップグレードである場合は、hint: ['client-device'] とともに "platform" に設定します。"platform" は、RP がプラットフォーム認証システム(プラットフォーム デバイスに組み込まれた認証システム)を求めていることを示します。この認証システムでは、たとえば USB セキュリティ キーの挿入を求めるプロンプトは表示されません。ユーザーは、より簡単な方法でパスキーを作成できます。
  • authenticatorSelection.requireResidentKey: ブール値 true に設定します。検出可能な認証情報(常駐キー)は、ユーザー情報をパスキーに保存し、認証時にユーザーがアカウントを選択できるようにします。
  • authenticatorSelection.userVerification: デバイスの画面ロックを使用するユーザー確認が、"required""preferred""discouraged" のいずれであるかを示します。デフォルトは "preferred" です。これは、認証システムがユーザー確認をスキップする可能性があることを意味します。"preferred" に設定するか、プロパティを省略します。

サーバーでオブジェクトを構築し、ArrayBuffer を Base64URL でエンコードして、フロントエンドから取得することをおすすめします。これにより、PublicKeyCredential.parseCreationOptionsFromJSON() を使用してペイロードをデコードし、navigator.credentials.create() に直接渡すことができます。

次のコード スニペットは、パスキーの作成に必要な情報を取得してデコードする方法を示しています。

// Fetch an encoded `PubicKeyCredentialCreationOptions` from the server.
const _options = await fetch('/webauthn/registerRequest');

// Deserialize and decode the `PublicKeyCredentialCreationOptions`.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseCreationOptionsFromJSON(decoded_options);
...

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

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

Browser Support

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

Source

// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
  publicKey: options
});

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

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

プロミスはさまざまな理由で拒否される可能性があります。これらのエラーは、Error オブジェクトの name プロパティをチェックすることで処理できます。

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

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

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

.toJSON() メソッドでオブジェクトをエンコードし、JSON.stringify() でシリアル化してサーバーに送信します。

...

// Encode and serialize the `PublicKeyCredential`.
const _result = credential.toJSON();
const result = JSON.stringify(_result);

// Encode and send the credential to the server for verification.  
const response = await fetch('/webauthn/registerResponse', {
  method: 'post',
  credentials: 'same-origin',
  body: result
});
...

認証情報を保存する

バックエンドで公開鍵認証情報を取得したら、公開鍵認証情報を処理する独自のコードを記述するのではなく、サーバーサイドのライブラリまたはソリューションを使用することをおすすめします

取得した認証情報は、後で使用するためにデータベースに保存できます。

次のリストは、保存することをおすすめするプロパティです。

  • 認証情報 ID: 公開鍵認証情報とともに返される認証情報 ID。
  • Credential name: 認証情報の名前。AAGUID に基づいて識別できる、作成元のパスキー プロバイダにちなんだ名前を付けます
  • ユーザー ID: パスキーの作成に使用されたユーザー ID。
  • 公開鍵: 公開鍵認証情報とともに返される公開鍵。これは、パスキー アサーションを検証するために必要です。
  • 作成日時: パスキーの作成日時を記録します。これは、パスキーを識別するのに役立ちます。
  • 最終使用日時: ユーザーがパスキーを使用してログインした最後の日時を記録します。これは、ユーザーがどのパスキーを使用したか(または使用しなかったか)を判断するのに役立ちます。
  • AAGUID: パスキー プロバイダの固有識別子。
  • バックアップの対象フラグ: デバイスがパスキーの同期の対象である場合は true。この情報は、パスキー管理ページで同期可能なパスキーとデバイスにバインドされた(同期不可能な)パスキーをユーザーが識別するのに役立ちます。

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

登録が失敗した場合にシグナルを送信する

パスキーの登録に失敗すると、ユーザーが混乱する可能性があります。パスキー プロバイダにパスキーがあり、ユーザーが利用できる状態でも、関連付けられた公開鍵がサーバーサイドに保存されていない場合、パスキーを使用したログイン試行は決して成功せず、トラブルシューティングも困難になります。その場合は、お客様に必ずお知らせします。

このような状況を防ぐには、Signal API を使用して、パスキー プロバイダに不明なパスキーを通知します。RP は、RP ID と認証情報 ID を指定して PublicKeyCredential.signalUnknownCredential() を呼び出すことで、指定された認証情報が削除されたか存在しないことをパスキー プロバイダに通知できます。このシグナルをどのように処理するかはパスキー プロバイダ次第ですが、サポートされている場合は、関連付けられたパスキーが削除されることが想定されます。

// Detect authentication failure due to lack of the credential
if (response.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.
    ...
  }
}

Signal API の詳細については、Signal API を使用して、パスキーとサーバー上の認証情報の整合性を維持するをご覧ください。

ユーザーに通知を送信する

パスキーが登録されたときに通知(メールなど)を送信すると、ユーザーはアカウントへの不正なアクセスを検知できます。攻撃者がユーザーの知らないうちにパスキーを作成した場合、パスワードが変更された後も、パスキーは不正使用に利用できる状態のままになります。通知はユーザーに警告し、この問題を回避するのに役立ちます。

チェックリスト

  • パスキーの作成を許可する前に、ユーザーを確認します(メールまたは安全な方法を使用することが望ましい)。
  • excludeCredentials を使用して、同じパスキー プロバイダの重複するパスキーが作成されないようにします。
  • パスキー プロバイダを特定し、ユーザーの認証情報を命名するために、AAGUID を保存します。
  • パスキーの登録が PublicKeyCredential.signalUnknownCredential() で失敗した場合にシグナルを送信します。
  • アカウントのパスキーを作成して登録した後、ユーザーに通知を送信します。

リソース

次のステップ: フォームの自動入力でパスキーを使用してログインする