passkeyはpasswordの代わりに公開鍵暗号を使うFIDO credentialです。siteは登録時に公開鍵を保存します。assertionの署名対象はchallenge文字列だけではありません。authenticatorはcredentialの秘密鍵でauthenticatorData || clientDataHashに署名します。clientDataHashは直列化されたclientDataJSONのSHA-256です。challengeとoriginはclientDataJSONに、RP ID hashはauthenticatorDataに含まれます。serverは各値を個別に検証し、登録済み公開鍵で署名を確認します。秘密鍵自体をserviceへ送ることはありません。W3C WebAuthn Level 3の署名データの説明を参照してください。FIDO Allianceのpasskey案内は同期型とdevice-bound型の違いを説明します。

assertionはchallengeを含むデータに署名する

登録ではRelying Partyがbrowserにcredential作成を求め、authenticatorがkey pairを生成して公開鍵credentialを返します。serviceはcredential ID、公開鍵、accountとの対応を保存します。login時にはserverが新しいchallengeを発行し、user確認後にauthenticatorが署名したassertionを返します。serverはchallenge、origin、RP IDなどを確認し、保存済み公開鍵で署名を検証します。fingerprintやPINそのものをweb siteへ渡すloginではありません。

同期とdiscoverableは別の性質

同期型passkeyは同じproviderに接続した別deviceでも利用できるよう同期されます。device-bound credentialはauthenticatorに結び付くため、そのdeviceを失うと別credentialがなければaccessを失うことがあります。FIDO Allianceのdeployment資料は、同期がdevice交換やrecoveryを容易にする一方、provider accountやdevice互換性の問題が残ると説明しています。

WebAuthnのdiscoverable credentialは、Relying Partyが先にcredential IDを指定しなくてもauthenticatorまたはclient platformが見つけられるcredentialです。以前のresident keyはこのdiscoverabilityを指す用語です。一方backup-eligible/multi-deviceとsingle-deviceは、credentialがbackup・同期できるかに関する区別です。residentと呼ばれるdiscoverable credentialでも、必ずdevice-boundとは限りません。

service側で検証する項目

毎回予測困難なchallengeを発行して再利用を拒否します。responseのoriginとRP IDが期待するsiteと一致するか確認し、accountに保存した公開鍵で署名を検証します。credential IDと公開鍵をaccountに紐付けて保存し、複数登録を管理するlabelや登録日を利用者に示すと整理しやすくなります。署名dataのparseだけで十分とせず、保守されているWebAuthn実装とbrowser API資料に従います。

登録時にrecoveryを決める

device交換やprovider移行に備え、使えなくなる前に第二のpasskeyまたは明確なaccount recovery方法を用意します。同期型でもprovider accountを失ったり、移行先deviceがproviderに対応しなかったりすると復元できないことがあります。device-bound credentialを中心にするserviceは、追加authenticatorの登録方法とdevice紛失後の対応を案内してください。FIDOの資料は、同期型・device-bound型のどちらでも代替認証とrecoveryを検討するよう勧めています。

参考資料