FIDO2 認証フローとインターフェース詳細
概要
このドキュメントは、W3C WebAuthn Level 2仕様のFigure 2 Authentication Flowに基づいて、FIDO2 認証フローの各インターフェース(①〜⑥)とパラメータを詳細に解説します。
情報源: W3C WebAuthn Level 2 - Figure 2 Authentication Flow
このドキュメントで学べること:
- Relying Party ServerとRP JavaScript間の通信(①、⑤)
- BrowserとAuthenticator間のWebAuthn API(②、④)
- Authenticator内部処理(③)
- RP Serverでの検証処理(⑥)
- 各インターフェースの標準化状況
アーキテクチャ図
┌─────────────────────────────────────────────────────────┐
│ Relying Party Server │
│ ⑥ server │
│ validation │
└───────────────┬──────────────────▲──────────────────────┘
│ │
① challenge ⑤ clientDataJSON,
authenticatorData,
signature
│ │
┌───────────────▼──────────────────┴──────────────────────┐
│ RP JavaScript Application │
│ (Webブラウザー内で実行) │
├─────────────────────────────────────────────────────────┤
│ Browser (User Agent) │
│ WebAuthn API実装 │
└───────────────┬──────────────────▲──────────────────────┘
│ │
② relying party id, ④ authenticatorData,
clientDataHash signature
│ │
┌───────────────▼──────────────────┴──────────────────────┐
│ Authenticator │
│ ③ user verification, │
│ create assertion │
└─────────────────────────────────────────────────────────┘
W3C WebAuthn仕様の標準化範囲
W3C WebAuthn仕様は、全てのインターフェースを標準化しているわけではありません。標準化の範囲を理解することが重要です。
✅ W3C WebAuthn仕様が標準化しているもの
| 項目 | 説明 | 標準化の目的 |
|---|---|---|
| JavaScript API(②、④) | navigator.credentials.get() のインターフェース | Browserの動作を統一(相互運用性) |
| データ構造 | authenticatorData、signature、clientDataJSON の構造 | RP ↔ Browser ↔ Authenticator間のデータ交換を統一 |
| 検証手順(⑥) | RPが実行すべき検証ステップ(Section 7.2) | セキュリティ要件の明確化 |
| 型定義 | PublicKeyCredentialRequestOptions 等の TypeScript/IDL 定義 | API仕様の明確化 |
標準化の範囲:
Browser(User Agent)の実装 = 完全に標準化
↓
・navigator.credentials.get() の動作
・authenticatorData の生成方法
・clientDataJSON の構造
・Authenticator との通信プロトコル(CTAP)
❌ W3C WebAuthn仕様が標準 化していないもの
| 項目 | 説明 | 理由 |
|---|---|---|
| RP Server ↔ RP JavaScript間の通信(①、⑤) | HTTPエンドポイント、リクエスト/レスポンス構造 | 各RPが独自のバックエンドAPI設計を採用できるようにするため |
| パラメータ名 | username / user_name / email 等 | RP内部の設計自由度を保つため |
| エンドポイントURL | /fido2-authentication-challenge 等 | RESTful設計やURL設計はRP次第 |
| 認証フロー全体 | OAuth 2.0連携、セッション管理等 | RPごとに認証アーキテクチャが異なるため |
非標準化の範囲:
RP Server ↔ RP JavaScript の通信 = 標準化なし
↓
・HTTPエンドポイントURL
・リクエストのJSON構造
・レスポンスのJSON構造
・パラメータ名
・エラーレスポンス形式
📖 W3C仕様の明確な記述
W3C WebAuthn Level 2 - Section 1.2 Conformance: "This specification does not define a server-side API; it only defines the client-side API."
日本語訳: "この仕様はサーバー側APIを定義していません。クライアント側APIのみを定義します。"
これの意味:
- ✅ Browserの動作(JavaScript API、データ構造)は完全に標準化
- ❌ RPのバックエンドAPI(①、⑤)は各実装の自由
なぜこのような設計なのか?
| 観点 | 理由 |
|---|---|
| 相互運用性 | Browserの動作を統一すれば、どのRPでも同じJavaScript APIで実装可能 |
| 柔軟性 | RPごとに異なるバックエンドアーキテクチャ(Node.js、Java、Python等)に対応 |
| 進化可能性 | RPのバックエンドは自由に進化できる(新機能追加、パフォーマンス改善等) |
| 責任分離 | W3CはBrowser実装を標準化し、RPはセキュリティ要件(検証手順)のみ遵守 |
実例: idp-server、Google、GitHub、Microsoftは全て異なるバックエンドAPI設計ですが、全て同じWebAuthn APIで動作します。
① RP Server → RP JavaScript: チャレンジ取得
通信: HTTP(各実装が自由に設計)
一般的なリクエスト
POST /fido2-authentication-challenge
Content-Type: application/json
{
"username": "user@example.com"
}
一般的なレスポンス
{
"challenge": "Y2hhbGxlbmdl...",
"rpId": "example.com",
"allowCredentials": [
{
"type": "public-key",
"id": "credential_id_base64url",
"transports": ["internal"]
}
],
"timeout": 60000,
"userVerification": "preferred"
}
主要パラメータ
| パラメータ | 型 | 説明 | 例 |
|---|---|---|---|
challenge | Base64URL | ランダムチャレンジ(32バイト以上推奨) | "Y2hhbGxlbmdl..." |
rpId | String | RPのドメイン名(省略時はcurrent origin) | "example.com" |
allowCredentials | Array | 許可するCredential IDリスト | [{type, id, transports}] |
timeout | Number | タイムアウト(ミリ秒) | 60000 |
userVerification | String | User Verification要件 | "required" / "preferred" / "discouraged" |
allowCredentialsの2つのパターン
| パターン | allowCredentials | ユーザー名入力 | 用途 |
|---|---|---|---|
| ユーザー名入力あり | RPがCredential IDを指定 | 必要 | 2要素認証、既存システムとの統合 |
| パスワードレス | 空配列 [] | 不要 | パスワードレスログイン(Discoverable Credential必須) |
詳細: basic-17: FIDO2・パスキー・Discoverable Credential
セキュリティ要件
- ✅
challengeは暗号学的に安全なランダム値(32バイト以上推奨) - ✅ サーバー側でチャレンジを一時保存(検証時に使用、1回のみ有効)
- ✅ チャレンジの有効期限を設定(例: 2分)
- ✅
allowCredentialsはユーザーに関連付けられたCredential IDのみ返す
② Browser → Authenticator: WebAuthn API呼び出し
通信: WebAuthn API(W3C標準)
JavaScriptコード
// ① で取得したレスポンスを変換
const publicKeyOptions = {
challenge: base64UrlToBuffer(serverResponse.challenge),
rpId: serverResponse.rpId,
allowCredentials: serverResponse.allowCredentials.map(cred => ({
type: cred.type,
id: base64UrlToBuffer(cred.id),
transports: cred.transports
})),
timeout: serverResponse.timeout,
userVerification: serverResponse.userVerification
};
// WebAuthn API呼び出し
const assertion = await navigator.credentials.get({
publicKey: publicKeyOptions
});
Browserから認証器へ渡されるデータ
| データ | 説明 | 由来 |
|---|---|---|
rpId | RPのドメイン名 | サーバーから受領 |
allowCredentials | 許可するCredential IDリスト | サーバーから受領 |
clientDataHash | clientDataJSONのSHA-256ハッシュ | Browser内部で生成 |
userVerification | User Verification要件 | サーバーから受領 |