FIDO2 識別子とIdP統合 - userId, username, credentialId の関係
概要
FIDO2/WebAuthn では複数の識別子が登場し、それぞれが異なる役割を持ちます。このドキュメントでは、これらの識別子の関係性と、IdP(Identity Provider)との統合における注意点を解説します。
このドキュメントで学べること:
- FIDO2 における3つの主要な識別子(userId, username, credentialId)
- 各識別子の発行者と保存先
- 登録・認証フローでの識別子の流れ
- IdP と FIDOサーバーの役割分担
- 実装パターンと設計上の考慮事項
FIDO2 の識別子一覧
3つの主要な識別子
┌─────────────────────────────────────────────────────────────────────────────┐
│ FIDO2 の識別子 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ userId │ │ username │ │ credentialId │ │
│ │ (user.id) │ │ (user.name) │ │ │ │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
│ │ バイト列 │ │ 文字列 │ │ バイト列 │ │
│ │ (Base64URL) │ │ │ │ (Base64URL) │ │
│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │
│ │ RP が発行 │ │ RP が決定 │ │ 認証器が生成 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
各識別子の詳細
| 識別子 | WebAuthn での名称 | 発行者 | 最大サイズ | 用途 |
|---|---|---|---|---|
| userId | user.id / userHandle | RP(IdP) | 64バイト | ユーザーの一意識別 |
| username | user.name | RP(IdP) | 任意 | 人間が読めるユーザー識別子 |
| credentialId | credential.id / id | 認証器 | 1023バイト | 公開鍵/秘密鍵ペアの識別 |
登録フローでの識別子の流れ
シーケンス図
┌──────────────────────────────────────────────────────────────────────────────┐
│ 登録フロー │
├───────────────── ─────────────────────────────────────────────────────────────┤
│ │
│ IdP (RP Server) クライアント (RP) ブラウザ 認証器 │
│ │ │ │ │ │
│ │ │ │ │ │
│ ┌────┴────┐ │ │ │ │
│ │ userId │ │ │ │ │
│ │ username│ ← IdP が決定 │ │ │ │
│ └────┬────┘ │ │ │ │
│ │ │ │ │ │
│ │ ① Challenge + │ │ │ │
│ │ user: { │ │ │ │
│ │ id: userId, │ │ │ │
│ │ name: username, │ │ │ │
│ │ displayName │ │ │ │
│ │ } │ │ │ │
│ │─────────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ ② create() │ │ │
│ │ │─────────────────────>│ │ │
│ │ │ │ │ │
│ │ │ │ ③ 認証器へ │ │
│ │ │ │────────────>│ │
│ │ │ │ │ │
│ │ │ │ ┌──────┴─────┐│
│ │ │ │ │ 鍵ペア生成 ││
│ │ │ │ │credentialId││
│ │ │ │ │ 生成 ││
│ │ │ │ │ ││
│ │ │ │ │ userId保存 ││
│ │ │ │ │ (※Passkey) ││
│ │ │ │ └──────┬───── ┘│
│ │ │ │ │ │
│ │ │ │ ④ 結果 │ │
│ │ │ │<────────────│ │
│ │ │ │ │ │
│ │ │ ⑤ 結果 │ │ │
│ │ │<─────────────────────│ │ │
│ │ │ │ │ │
│ │ ⑥ id: credentialId, │ │ │ │
│ │ attestationObject, │ │ │ │
│ │ clientDataJSON │ │ │ │
│ │<──── ─────────────────────│ │ │ │
│ │ │ │ │ │
│ ┌────┴────┐ │ │ │ │
│ │検証+保存│ │ │ │ │
│ │userId │ │ │ │ │
│ │ ↓ │ │ │ │ │
│ │credential │ │ │ │
│ │ Id │ │ │ │ │
│ └─────────┘ │ │ │ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
登録時のデータフロー
① RP Server → クライアント(Challenge レスポンス)
{
"challenge": "ランダムなチャレンジ(Base64URL)",
"rp": {
"id": "example.com",
"name": "Example Corporation"
},
"user": {
"id": "dXNlci0xMjM0NTY3ODkw", // ← IdP が発行した userId (Base64URL)
"name": "user@example.com", // ← username
"displayName": "田中 太郎" // ← 表示名
},
"pubKeyCredParams": [...],
"timeout": 60000,
"attestation": "none"
}
重要: user.id と user.name は IdP(RP Server)が決定します。
⑥ クライアント → RP Server(登録レスポンス)
{
"id": "Y3JlZGVudGlhbElkLTEyMzQ1Njc4OTA...", // ← credentialId(認証器が生成)
"type": "public-key",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIi...",
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRo..."
}
}
重要: id(credentialId)は認証器が生成した値です。クライアントから送られてきます。
認証フローでの識別子の流れ
シーケンス図
┌──────────────────────────────────────────────────────────────────────────────┐
│ 認証フロー │
├───── ─────────────────────────────────────────────────────────────────────────┤
│ │
│ IdP (RP Server) クライアント (RP) ブラウザ 認証器 │
│ │ │ │ │ │
│ │ ① Challenge + │ │ │ │
│ │ allowCredentials │ │ │ │
│ │─────────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ ② get() │ │ │
│ │ │─────────────────────>│ │ │
│ │ │ │ │ │
│ │ │ │ ③ 認証器へ │ │
│ │ │ │────────────>│ │
│ │ │ │ │ │
│ │ │ │ ┌──────┴─────┐│
│ │ │ │ │ 署名生成 ││
│ │ │ │ │ ││
│ │ │ │ │ userHandle ││
│ │ │ │ │ 返却 ││
│ │ │ │ │ (※Passkey) ││
│ │ │ │ └──────┬─────┘│
│ │ │ │ │ │
│ │ │ │ ④ 結果 │ │
│ │ │ │<────────────│ │
│ │ │ │ │ │
│ │ │ ⑤ 結果 │ │ │
│ │ │<─────────────────────│ │ │
│ │ │ │ │ │
│ │ ⑥ id: credentialId, │ │ │ │
│ │ signature, │ │ │ │
│ │ userHandle, │ │ │ │
│ │ authenticatorData │ │ │ │
│ │<─────────────────────────│ │ │ │
│ │ │ │ │ │
│ ┌────┴────┐ │ │ │ │
│ │署名検証 │ │ │ │ │
│ │ │ │ │ │ │
│ │credentialId │ │ │ │
│ │ → 公開鍵特定 │ │ │ │
│ │ │ │ │ │ │
│ │userHandle │ │ │ │
│ │ → ユーザー特定 │ │ │ │
│ │ (※Passkey) │ │ │ │
│ └─────────┘ │ │ │ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
認証時の識別子の役割
| 識別子 | 認証時の役割 | 使用シナリオ |
|---|---|---|
| credentialId | 公開鍵の特定 | 常に使用 |
| userHandle | ユーザーの特定 | Passkey(Discoverable Credential)の場合 |
| username | (使用しない) | 認証前にユーザー名入力がある場合のみ |
ユーザー特定の方式
方式1: 2要素認証(ユーザー既知)
1. ユーザーがユーザー名/パスワードで認証
2. IdP がユーザーを特定
3. そのユーザーの credentialId リストを allowCredentials に設定
4. FIDO2 認証を実行
5. 署名検証のみ(ユーザーは既に特定済み)