パスワードレス認証
前提知識
このドキュメントを理解するには、以下の基礎知識が役立ちます:
- 認証ポリシー - 認証方式の設定
- 多要素認証(MFA) - 認証要素の概念
- FIDO2/WebAuthnの基礎 - 技術的な背景
概要
idp-serverは、パスワードレス認証をサポートします。
パスワードレス認証とは、パスワードを使用せずにユーザーを認証する方式です。生体認証やセキュリティキーなど、より安全で使いやすい認証手段を提供します。
ユーザー ──[生体認証/セキュリティキー]──> 認証器 ──[署名]──> idp-server ──[検証]──> 認証成功
idp-serverでは以下のパスワードレス認証方式に対応しています:
- FIDO2/WebAuthn: 生体認証、セキュリティキー
- Passkey: デバイス間で同期可能な認証資格情報
- FIDO UAF: モバイルアプリ向け生体認証(CIBA連携)
なぜパスワードレス認証が必要か
パスワードの課題
パスワード認証には多くの課題があります:
| 課題 | 内容 | 影響 |
|---|---|---|
| フィッシング | 偽サイトでパスワードを盗まれる | アカウント乗っ取り |
| 使い回し | 同じパスワードを複数サイトで使用 | 漏洩時の被害拡大 |
| 覚えられない | 複雑なパスワードは記憶困難 | ユーザー体験の低下 |
| 管理コスト | リセット対応、ポリシー管理 | 運用負荷 |
パスワードレス認証のメリット
| メリット | 説明 |
|---|---|
| フィッシング耐性 | 認証器がオリジン(ドメイン)を検証するため、偽サイトでは認証不可 |
| 利便性 | 指紋や顔認証でワンタッチ認証 |
| セキュリティ | 秘密鍵は認証器から出ない |
| 運用コスト削減 | パスワードリセット対応が不要 |
idp-serverにおけるパスワードレス認証
1. FIDO2/WebAuthn
WebAuthn(Web Authentication API)は、W3Cが標準化したパスワードレス認証の仕様です。
ユーザー ブラウザ idp-server 認証器
| | | |
|--ログインボタン->| | |
| |--認証開始------->| |
| |<--challenge-----| |
| |--認証要求------------------------>|
|<--生体認証-------------------------------------|
|--指紋/顔---------------------------------------->|
| |<--署名---------------------------|
| |--署名検証------>| |
| |<--認証成功------| |
対応認証器:
- プラットフォーム認証器: Touch ID, Face ID, Windows Hello
- ローミング認証器: YubiKey, セキュリティキー
設定方法: FIDO2設定ガイド
2. Passkey
Passkeyは、FIDO2の拡張で、デバイス間で認証資格情報を同期できる機能です。
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ Apple │ │ Google │
│ │ │ │
│ iPhone ──┐ │ │ Android ──┐ │
│ ├──> iCloud │ │ ├ ──> Google │
│ Mac ─────┘ Keychain │ │ Chrome ───┘ Password │
│ │ │ Manager │
└─────────────────────────────┘ └─────────────────────────────┘
メリット:
- デバイス紛失時も他のデバイスで認証可能
- 新しいデバイスへの移行が容易
- ユーザー体験の向上
詳細: Passkeyの基礎
ユーザーとパスキーの関係
データモデル
idp-serverでは、ユーザーとパスキー(FIDO2クレデンシャル)は以下の関係で管理されます。1デバイス = 1クレデンシャルの設計を採用しています。
User (ユーザー)
└── AuthenticationDevice (認証デバイス) [1:N]
├── credential_type: クレデンシャルタイプ (fido2, jwt_bearer等)
├── credential_id: クレデンシャルID
├── credential_payload: クレデンシャル固有データ
│ └── FIDO2の場合:
│ - credential_id: WebAuthnクレデンシャルID
│ - rp_id: Relying Party ID
│ - fido_server_id: FIDOサーバーID
│ - public_key: 公開鍵
└── credential_metadata: メタデータ(登録日時等)
関係性
| エンティティ | 説明 | 関係 |
|---|---|---|
| User | ユーザーアカウント | 1ユーザーに複数の認証デバイスを登録可能 |
| AuthenticationDevice | 認証に使用するデバイス(iPhone、Mac等) | 1デバイス = 1クレデンシャル(統合設計) |
1デバイス = 1クレデンシャル設計
この設計には以下のメリットがあります:
| メリット | 説明 |
|---|---|
| シンプルなデータモデル | デバイスとクレデンシャルの1:1対応で管理が容易 |
| 直感的なUI | ユーザーはデバイス単位でパスキーを管理 |
| 効率的なクエリ | JOINなしでデバイスとクレデンシャルを取得可能 |
| 明確なライフサイクル | デバイス削除 = クレデンシャル削除 |
複数のパスキーを登録する場合は、それぞれ別のAuthenticationDeviceとして登録されます。
制約事項
| 制約 | 内容 |
|---|---|
| rpIdの一致 | 登録時のrpIdと認証時のrpIdが一致する必要がある |
| rpIdのスコープ | rpIdは現在のドメインまたはその親ドメインのみ指定可能 |
| クレデンシャルの一意性 | 同一rpId内でcredential_idは一意 |
| 1:1対応 | 1つのAuthenticationDeviceに1つのクレデンシャルのみ |
FIDO2のusername
パスキー登録・認証時に使用されるusernameの仕様です。
WebAuthn仕様の詳細: FIDO2登録フローとインターフェースを参照してください。
アーキテクチャ
idp-serverは、FIDOサーバーの実装を抽象化しています。
┌─────────────────────────────────────────────────────────────┐
│ idp-server │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Fido2RegistrationInteractor │ │
│ │ Fido2AuthenticationInteractor │ │
│ │ └── username解決(TenantIdentityPolicy) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────────────┴────────────┐ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ WebAuthn4j Adapter │ │ External FIDO Server │ │
│ │ (built-in) │ │ (http_request経由) │ │
│ └─────────────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
usernameの決定ルール
TenantIdentityPolicyのuniqueKeyType設定に基づいてusernameを決定します。
| uniqueKeyType | usernameの値 | 用途 |
|---|---|---|
USERNAME | preferredUsername | 社内システム(従業員ID) |
EMAIL | email | 一般向けWebサービス(デフォルト) |
PHONE | phoneNumber | モバイルアプリ(SMS認証) |
EXTERNAL_USER_ID | externalUserId | 外部IdP連携 |
この解決はFido2RegistrationInteractorで行われ、FIDOサーバーの実装(WebAuthn4j / 外部サーバー)に依存しません。
usernameの流れ
┌──────────────────────────────────────────────────────────────┐
│ パスキー登録フロー │
├──────────────────────────────────────────────────────────────┤
│ 1. フロントエ ンド → idp-server │
│ └── username を送信(TenantIdentityPolicyに基づく値) │
│ │
│ 2. idp-server → FIDOサーバー │
│ └── username を含むリクエストを転送 │
│ └── WebAuthn4j または 外部FIDOサーバー │
│ │
│ 3. FIDOサーバー → 認証器 │
│ └── user.name として認証器UIに表示・保存 │
│ │
│ 4. FIDOサーバー → idp-server │
│ └── レスポンスにusernameを含める │
│ └── metadata.username_param で取得キーを指定 │
└──────────────────────────────────────────────────────────────┘
FIDO2設定
metadata.username_paramで、FIDOサーバーのレスポンスからusernameを取得するパラメータ名を指定します。
WebAuthn4j Adapter(built-in)の場合:
{
"type": "fido2",
"metadata": {
"username_param": "username"
},
"interactions": {
"fido2-registration": {
"execution": {
"function": "webauthn4j_registration"
}
}
}
}
外部FIDOサーバーの場合:
{
"type": "fido2",
"metadata": {
"username_param": "user_id"
},
"interactions": {
"fido2-registration": {
"execution": {
"function": "http_request",
"http_request": {
"url": "https://fido-server.example.com/registration"
}
}
}
}
}
外部FIDOサーバーを使用する場合、レスポンスのどのフィールドにusernameが含まれるかはサーバー実装に依存するため、username_paramで適切に指定してください。
注意事項
| 注意点 | 説明 |
|---|---|
| 一意性 | usernameはテナント内で一意である必要がある |
| 不変性 | 登録後のusername変更は新規パスキー登録が必要 |
| 64バイト制限 | WebAuthn仕様により、認証器で切り詰められる可能性あり |
| FIDOサーバー依存 | 外部サーバー使用時はusername_paramの設定が重要 |
rpIdとサブドメインの関係
WebAuthn仕様では、rpIdの有効性は以下のルールで判定されます。
| ケース | 例 | 有効性 |
|---|---|---|
| 完全一致 | ホスト: auth.local.test / rpId: auth.local.test | 有効 |
| 親ドメイン | ホスト: auth.local.test / rpId: local.test | 有効 |
| 兄弟ドメイン | ホスト: auth.local.test / rpId: api.local.test | 無効 |
| 無関係なドメイン | ホスト: auth.local.test / rpId: example.com | 無効 |
推奨: サブドメイン構成では、親ドメイン(例: local.test)をrpIdとして設定することで、複数のサブドメイン間でパスキーを共有できます。
1ユーザー複数パスキー
ユーザーは複数のパスキーを登録できます。各パスキーは個別のAuthenticationDeviceとして管理されます:
- バックアップ用: デバイス紛失時のリカバリー
- 複数デバイス: iPhone、Mac、セキュリティキーなど
- 異なるrpId: サブドメインごとに異なるパスキー(非推奨)
User: alice@example.com
├── AuthenticationDevice: "iPhone 15"
│ ├── credential_type: "fido2"
│ └── credential_payload: { rp_id: "example.com", ... }
├── AuthenticationDevice: "MacBook Pro"
│ ├── credential_type: "fido2"
│ └── credential_payload: { rp_id: "example.com", ... }
└── AuthenticationDevice: "YubiKey 5"
├── credential_type: "fido2"
└── credential_payload: { rp_id: "example.com", ... }