クライアント認証実装ガイド
このドキュメントの目的
クライアント認証(Client Authentication)の仕組みと7つの認証方式を理解することが目標です。
所要時間
⏱️ 約30分
前提知識
- 03. Token Flow
- OAuth 2.0基礎知識
クライアント認証とは
クライアント(アプリケーション)の正当性を検証する仕組み。
使用される場面
| エンドポイント | 用途 | 認証必須度 |
|---|---|---|
| Token Request | トークン発行 | 必須(Confidential Client) |
| CIBA認証リクエスト | バックチャネル認証開始 | 必須 |
| Token Introspection | トークン検証 | 推奨 |
| Token Revocation | トークン失効 | 推奨 |
Public Client(SPA/Mobile): client_secret_none + PKCE必須
アーキテクチャ全体像
クライアント認証の処理フロー
Token Request / CIBA Request等
↓
TokenRequestHandler / CibaRequestHandler
↓
┌──────────────────────────────────────────── ─────────┐
│ TokenRequestContext作成 │
├─────────────────────────────────────────────────────┤
│ - clientSecretBasic: Authorizationヘッダーから抽出 │
│ - clientCert: x-ssl-certヘッダーから抽出(MTLS) │
│ - parameters: POSTボディ │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ ClientAuthenticationHandler.authenticate() │
├─────────────────────────────────────────────────────┤
│ 1. クライアント認証方式の検出 │
│ - Authorizationヘッダー存在 → client_secret_basic│
│ - client_assertionパラメータ → JWT認証 │
│ - client_idのみ → none │
│ - x-ssl-cert存在 → MTLS │
│ │
│ 2. ClientAuthenticators.get(認証タイプ) │
│ → 認証方式別のAuthenticator取得(Plugin) │
│ │
│ 3. Authenticator.authenticate() │
│ → クライアント認証実行 │
└─────────────────────────────────────────────────────┘
↓
ClientCredentials(認証済み情報)
- clientId: 認証済みクライアントID
- authenticationMethod: 使用した認証方式
実装: ClientAuthenticationHandler.java
7つの認証方式
標準認証方式(5種類)
| 認証方式 | 送信方法 | セキュリティ | 用途 |
|---|---|---|---|
| client_secret_basic | Basic認証ヘッダー | ⭐⭐ | 最も一般的(サーバーサイド) |
| client_secret_post | POSTボディ | ⭐ | レガシー対応 |
| client_secret_jwt | JWT署名(共有鍵HMAC) | ⭐⭐⭐ | 高セキュリティ |
| private_key_jwt | JWT署名(秘密鍵RSA/ECDSA) | ⭐⭐⭐⭐ | 最高セキュリティ |
| none | 認証なし | - | Public Client(SPA/Mobile+PKCE) |
FAPI拡張認証方式(2種類)
| 認証方式 | 送信方法 | セキュリティ | 用途 |
|---|---|---|---|
| tls_client_auth | クライアント証明書(MTLS) | ⭐⭐⭐⭐⭐ | 金融機関・FAPI準拠 |
| self_signed_tls_client_auth | 自己署名証明書(MTLS) | ⭐⭐⭐⭐ | FAPI準拠・開発環境 |
拡張方式: FAPIモジュールロード時のみ有効(Plugin)
1. client_secret_basic(最も一般的)
実装: ClientSecretBasicAuthenticator.java
リクエスト例
curl -X POST "http://localhost:8080/${TENANT_ID}/v1/tokens" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic $(echo -n 'my-client:my-secret' | base64)" \
-d "grant_type=authorization_code&code=${CODE}&redirect_uri=${REDIRECT_URI}"
処理フロー
1. Authorizationヘッダー取得
Authorization: Basic Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ=
2. Base64デコード
Y2xpZW50LWlkOmNsaWVudC1zZWNyZXQ= → "client-id:client-secret"
3. コロンで分割
→ clientId="client-id", clientSecret="client-secret"
4. ClientConfiguration取得
clientConfigurationQueryRepository.get(tenant, clientId)
5. client_secret検証
if (clientConfiguration.clientSecret().equals(clientSecret)) {
認証成功
} else {
invalid_client エラー
}
注意点
Base64エンコード時の注意:
# ✅ 正しい(-n オプション付き)
echo -n 'my-client:my-secret' | base64
# ❌ 間違い(改行が入る)
echo 'my-client:my-secret' | base64
2. client_secret_post
実装: ClientSecretPostAuthenticator.java
リクエスト例
curl -X POST "http://localhost:8080/${TENANT_ID}/v1/tokens" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=${CODE}&redirect_uri=${REDIRECT_URI}&client_id=my-client&client_secret=my-secret"
処理フロー
1. POSTボディからパラメータ取得
client_id=my-client
client_secret=my-secret
2. ClientConfiguration取得
3. client_secret検証
→ 成功 or invalid_client
注意点
- ⚠️ セキュリティリスク: client_secretがHTTPボディに平文で含まれる
- ⚠️ 推奨しない: client_secret_basicを使用すべき