FIDO2 / WebAuthn Authentication Ceremony

AuthenticatorがRelying Partyのchallengeへ秘密鍵で署名し、パスワードを共有せずユーザーのCredential保有を証明するフローです。

APIcredentials.get()
ProofSigned challenge
PhishingOrigin-bound

このフローで、
何をするの?

このフローのゴール

端末の生体認証やPINを使い、パスワードに頼らず本人確認します。

01まず全体像

サーバーが一度だけ使えるchallengeを送り、端末のAuthenticatorがユーザー操作を確認して署名します。秘密鍵や生体情報は端末の外へ送られません。

02身近な例で考える

本人しか操作できない鍵で、サーバーから届いた一回限りの確認書へサインするイメージです。

03最後にどうなる?

サーバーが公開鍵で署名を確認できたときだけ、登録またはログインを完了します。

登場人物

図では、縦の列ごとに担当者やシステムを分けています。

U
User

サービスを利用する人です。ログインや同意、端末の操作を行います。

B
Browser

WebサイトとAuthenticatorの間を安全につなぎ、WebAuthn APIを実行します。

A
Authenticator

秘密鍵を安全に保管し、生体認証やPINの確認と署名を行う端末機能です。

R
Relying Party

ユーザーをログインさせるWebサービスです。公開鍵を保存し、署名を検証します。

図の読み方: 光る丸が今説明している通信です。自動再生を止めたいときは「Pause」を押し、気になる矢印を選んでください。

Interactive sequence
UUserAuthorization Gesture
BBrowserWebAuthn Client
AAuthenticatorPlatform / Roaming
RRelying PartyApplication Server
01

User Relying Party

サインイン開始

このステップで行うこと

ユーザーがPasskeyによるサインインを開始します。Discoverable Credentialならユーザー名を省略できます。

なぜ必要?

アカウント有無をOptions応答の違いから漏らさないようにします。

このあと

次は「要求Optionsを返却」へ進みます。

REQUEST / RESPONSE EXAMPLEBrowser → Relying Party
REQUEST
POST /webauthn/authentication/options
Content-Type: application/json
Accept: application/json
Cookie: session=...

{ "username": "user@example.com" }
レスポンス
HTTP/1.1 200 OK
Content-Type: application/json
Accept: application/json

{
  "challenge": "Y2hhbGxlbmdl...",
  "rpId": "example.com",
  "allowCredentials": [{ "type": "public-key", "id": "AbCdEf..." }],
  "userVerification": "required"
}

※ トークン、challenge、Credential IDなどは説明用に短縮したサンプル値です。

同じ仕様のフローを、続けて理解する。