CIBA Flow実装ガイド
このドキュメントの目的
**CIBA(Client Initiated Backchannel Authentication)**の実装を理解することが目標です。
所要時間
⏱️ 約30分
前提知識
- 02. Authorization Flow
- 03. Token Flow
- CIBA仕様基礎知識
CIBAとは
バックチャネル認証 - ユーザーがログインデバイスとは別のデバイス(スマホ等)で認証を承認する方式。
OpenID Connect CIBA Core 1.0準拠
通常のAuthorization Code Flowとの違い
| 項目 | Authorization Code Flow | CIBA |
|---|---|---|
| 認証デバイス | ブラウザ(同じデバイス) | スマホ等(別デバイス) |
| リダイレクト | あり(ブラウザリダイレクト) | なし |
| ユーザー操作 | ブラウザでログイン | スマホでプッシュ通知承認 |
| ポーリング | なし | あり(またはPing/Push) |
アーキテクチャ全体像
30秒で理解する全体像
HTTPリクエスト
↓
Controller (CibaV1Api) - HTTP処理
↓
EntryService (CibaFlowEntryService) - オーケストレーション
├─ Tenant取得
├─ CibaRequest作成
├─ CibaProtocol.request()(UserHintResolver使用)
├─ AuthenticationTransaction作成
├─ デフォルト認証実行(プッシュ通知)
├─ イベント発行
└─ auth_req_id返却
↓
Core層 (CibaProtocol)
├─ Validator: 入力形式チェック
├─ UserHintResolver: login_hint → User解決
├─ Verifier: ビジネスルール検証
└─ BackchannelAuthenticationRequest生成
↓
プッシュ通知 → ユーザー承認 → トークン発行(Token Flow)
主要クラスの責務
| クラス | 層 | 役割 | 実装 |
|---|---|---|---|
| CibaV1Api | Controller | HTTPエンドポイント | CibaV1Api.java |
| CibaFlowEntryService | UseCase | トランザクション・オーケストレーション | CibaFlowEntryService.java:86-142 |
| CibaProtocol | Core | CIBA仕様準拠処理 | Extension Core |
| UserHintResolver | Core | login_hint → User解決(Plugin) | Extension Core |
| BackchannelAuthenticationRequest | Core | CIBA認証リクエスト(5分TTL) | Extension Core |
| AuthenticationTransaction | Core | 認証状態管理 | Core Domain |
CIBAの特徴
Authorization Code Flowとの違い:
- ❌ リダイレクトなし: ユーザーは別デバイスで承認
- ✅ auth_req_id: Authorization Codeの代わり
- ✅ ポーリング: クライアントが定期的にトークンリクエスト
- ✅ プッシュ通知: FCM/APNS/SMSでユーザーに通知
- ✅ 非同期: 認証リクエストとトークン取得が分離
CIBAフロー
1. [クライアント] CIBA認証リクエスト
POST /{tenant-id}/v1/backchannel/authentications
{
"login_hint": "user@example.com",
"binding_message": "Code: 1234",
"client_notification_token": "xxx" // Push mode
}
↓
2. [idp-server] auth_req_id返却
{
"auth_req_id": "auth-req-abc123",
"expires_in": 300,
"interval": 5 // Poll mode
}
↓
3. [idp-server] ユーザーにプッシュ通知送信
FCM/APNS → [ユーザーのスマホ]
「Code: 1234でログイン承認しますか?」
↓
4. [ユーザー] スマホで承認
↓
5. [idp-server] 認証完了を記録
↓
6. [クライアント] トークンリクエスト(ポーリング)
POST /{tenant-id}/v1/tokens
{
"grant_type": "urn:openid:params:grant-type:ciba",
"auth_req_id": "auth-req-abc123",
"client_id": "xxx",
"client_secret": "yyy"
}
↓
7. [idp-server] Access Token + ID Token発行
{
"access_token": "eyJ...",
"id_token": "eyJ...",
"token_type": "Bearer"
}
エンドポイント
# CIBA認証リクエスト
POST /{tenant-id}/v1/backchannel/authentications
# トークン取得(ポーリング)
POST /{tenant-id}/v1/tokens
{
"grant_type": "urn:openid:params:grant-type:ciba",
"auth_req_id": "xxx"
}
実装:
EntryService実装
実装: CibaFlowEntryService.java:86-142
CIBA認証リクエスト処理(10ステップ)
@Transaction
public class CibaFlowEntryService implements CibaFlowApi {
CibaProtocols cibaProtocols;
UserHintResolvers userHintResolvers;
AuthenticationInteractors authenticationInteractors;
CibaRequestAdditionalVerifiers additionalVerifiers;
TenantQueryRepository tenantQueryRepository;
AuthenticationTransactionCommandRepository authenticationTransactionCommandRepository;
AuthenticationPolicyConfigurationQueryRepository authenticationPolicyConfigurationQueryRepository;
CibaFlowEventPublisher eventPublisher;
@Override
public CibaRequestResponse request(
TenantIdentifier tenantIdentifier,
Map<String, String[]> params,
String authorizationHeader,
String clientCert,
RequestAttributes requestAttributes) {
// 1. Tenant取得
Tenant tenant = tenantQueryRepository.get(tenantIdentifier);
// 2. CibaRequest作成
CibaRequest cibaRequest = new CibaRequest(tenant, authorizationHeader, params);
cibaRequest.setClientCert(clientCert);
// 3. CibaProtocol取得
CibaProtocol cibaProtocol = cibaProtocols.get(tenant.authorizationProvider());
// 4. Core層に委譲(UserHintResolver + AdditionalVerifiers使用)
CibaIssueResponse issueResponse =
cibaProtocol.request(cibaRequest, userHintResolvers, additionalVerifiers);
// 5. エラー時は即座に返却
if (!issueResponse.isOK()) {
return issueResponse.toErrorResponse();
}
// 6. イベント発行(CIBA認証リクエスト成功)
eventPublisher.publish(
tenant,
issueResponse.request(),
issueResponse.user(),
DefaultSecurityEventType.backchannel_authentication_request_success.toEventType(),
requestAttributes);
// 7. AuthenticationPolicyConfiguration取得
AuthenticationPolicyConfiguration authenticationPolicyConfiguration =
authenticationPolicyConfigurationQueryRepository.find(
tenant, StandardAuthFlow.CIBA.toAuthFlow());
// 8. AuthenticationTransaction作成
AuthenticationTransaction authenticationTransaction =
CibaAuthenticationTransactionCreator.create(
tenant, issueResponse, authenticationPolicyConfiguration);
// 9. デフォルト認証実行(プッシュ通知送信)
AuthenticationInteractionType authenticationInteractionType =
issueResponse.defaultCibaAuthenticationInteractionType();
AuthenticationInteractor authenticationInteractor =
authenticationInteractors.get(authenticationInteractionType);
AuthenticationInteractionRequestResult interactionRequestResult =
authenticationInteractor.interact(
tenant,
authenticationTransaction,
authenticationInteractionType,
new AuthenticationInteractionRequest(Map.of()),
requestAttributes,
userQueryRepository);
// 10. AuthenticationTransaction保存 + イベント発行
AuthenticationTransaction updatedTransaction =
authenticationTransaction.updateWith(interactionRequestResult);
authenticationTransactionCommandRepository.register(tenant, updatedTransaction);
eventPublisher.publish(
tenant,
issueResponse.request(),
issueResponse.user(),
interactionRequestResult.eventType(),
requestAttributes);
return issueResponse.toResponse();
}
}
ポイント
- ✅ UserHintResolvers: login_hint(email/phone/sub)からUserを解決(Plugin拡張可能)
- ✅ CibaRequestAdditionalVerifiers: CIBA固有の検証(パスワード必須等)
- ✅ デフォルト認証実行: CIBA認証リクエスト時点でプッシュ通知を送信
- ✅ AuthenticationTransaction: 認証状態を管理(ユーザー承認待ち)
- ✅ イベント発行: 2回(リクエスト成功・プッシュ通知送信)