第7章 OpenID ConnectとSAML

なぜこの章が重要か

「一度のログインですべてのシステムが使える」- この理想を実現するのがシングルサインオン(SSO)です。企業環境では、従業員が日々10以上のシステムを使うことも珍しくありません。OpenID ConnectとSAMLは、このSSO実現の中核技術です。しかし、両者の選択を誤ると、セキュリティリスクや運用コストの増大を招きます。この章では、フェデレーション認証の本質を理解し、適切な技術選択と実装方法を学びます。

7.1 フェデレーション認証の概念 - 組織間連携の必要性

7.1.1 なぜフェデレーション認証が必要なのか

class FederationAuthenticationConcept:
    """フェデレーション認証の概念と必要性"""
    
    def explain_traditional_problems(self):
        """従来の認証の問題点"""
        
        return {
            'password_proliferation': {
                'problem': 'パスワードの氾濫',
                'scenario': '''
                ある企業の従業員の1日:
                1. 社内ポータル:独自のID/パスワード
                2. メールシステム:別のID/パスワード
                3. 経費精算システム:また別のID/パスワード
                4. SaaSサービスA:個別のアカウント
                5. SaaSサービスB:さらに別のアカウント
                ...
                
                結果:平均15個のパスワードを管理
                ''',
                'consequences': [
                    'パスワードの使い回し',
                    '付箋にパスワードを書く',
                    '頻繁なパスワードリセット',
                    'ヘルプデスクの負担増大'
                ]
            },
            
            'identity_silos': {
                'problem': 'アイデンティティのサイロ化',
                'illustration': '''
                ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
                │  System A   │  │  System B   │  │  System C   │
                ├─────────────┤  ├─────────────┤  ├─────────────┤
                │ User: john  │  │ User: j.doe │  │ User: jdoe  │
                │ Dept: Sales │  │ Dept: 営業  │  │ Dept: SLS   │
                │ Role: Mgr   │  │ Role: 課長  │  │ Role: M     │
                └─────────────┘  └─────────────┘  └─────────────┘
                
                同じ人物の情報が異なる形式で重複管理
                ''',
                'issues': [
                    '情報の不整合',
                    '更新の手間(退職時など)',
                    'コンプライアンスリスク',
                    '監査の困難さ'
                ]
            },
            
            'cross_organization_collaboration': {
                'problem': '組織間連携の困難さ',
                'real_world_example': '''
                # 企業間プロジェクトのシナリオ
                
                企業A:プロジェクトオーナー
                企業B:開発パートナー
                企業C:デザインパートナー
                
                課題:
                - 企業Aのシステムに企業B,Cの従業員がアクセス必要
                - 各企業のセキュリティポリシーを満たす必要
                - プロジェクト終了時にアクセス権を確実に削除
                
                従来の解決策:
                - ゲストアカウントの大量発行
                - VPNの設定
                - 手動でのアカウント管理
                
                問題:管理コスト、セキュリティリスク、柔軟性の欠如
                '''
            }
        }

7.1.2 フェデレーション認証が解決する課題

class FederationSolution:
    """フェデレーション認証のソリューション"""
    
    def core_principles(self):
        """フェデレーション認証の中核原則"""
        
        return {
            'trust_relationship': {
                'concept': '信頼関係の確立',
                'explanation': '''
                # 信頼の連鎖
                
                User ─認証→ Identity Provider (IdP)
                              ↓
                         信頼関係
                              ↓
                Service Provider (SP) ←アサーション─ IdP
                
                SPはIdPを信頼し、IdPの認証結果を受け入れる
                ''',
                'benefits': [
                    'ユーザーは一箇所(IdP)でのみ認証',
                    'SPは認証機能を持つ必要なし',
                    '組織の境界を越えた連携が可能'
                ]
            },
            
            'separation_of_concerns': {
                'concept': '責任の分離',
                'roles': {
                    'identity_provider': {
                        'responsibility': 'ユーザーの認証',
                        'functions': [
                            'クレデンシャルの管理',
                            '多要素認証の実施',
                            'アイデンティティ情報の管理'
                        ]
                    },
                    'service_provider': {
                        'responsibility': 'サービスの提供',
                        'functions': [
                            'リソースの提供',
                            '認可の判断',
                            'ビジネスロジックの実行'
                        ]
                    },
                    'user': {
                        'responsibility': '適切な利用',
                        'experience': 'シームレスなアクセス'
                    }
                }
            },
            
            'standardization': {
                'concept': '標準プロトコルの使用',
                'importance': '''
                なぜ標準化が重要か:
                
                1. 相互運用性
                   - 異なるベンダーの製品間で連携可能
                   - 実装の選択肢が増える
                
                2. セキュリティ
                   - 広くレビューされた仕様
                   - 既知の脆弱性への対策
                
                3. 開発効率
                   - ライブラリの利用
                   - ベストプラクティスの共有
                
                4. 将来性
                   - ベンダーロックインの回避
                   - 技術の進化への対応
                '''
            }
        }
    
    def implementation_benefits(self):
        """実装によって得られる利益"""
        
        return {
            'user_experience': {
                'single_sign_on': {
                    'description': '一度の認証で複数サービス利用',
                    'flow': '''
                    朝9:00 - 社内ポータルにログイン
                    ↓
                    9:30 - メールシステム → 自動的にアクセス可能
                    ↓
                    10:00 - SaaSサービスA → 再認証不要
                    ↓
                    14:00 - SaaSサービスB → まだセッション有効
                    ''',
                    'satisfaction': '認証の手間が1/10以下に削減'
                },
                
                'consistent_experience': {
                    'description': '一貫した認証体験',
                    'benefits': [
                        '同じ認証画面',
                        '統一されたMFA',
                        '共通のパスワードポリシー'
                    ]
                }
            },
            
            'administrative_benefits': {
                'centralized_management': {
                    'description': '集中管理',
                    'capabilities': '''
                    # 管理者の操作
                    def onboard_employee(employee):
                        # IdPで一度だけ作成
                        idp.create_user(employee)
                        
                        # 自動的にすべてのSPで利用可能に
                        # 個別のアカウント作成不要
                    
                    def offboard_employee(employee):
                        # IdPで無効化
                        idp.disable_user(employee)
                        
                        # すべてのSPへのアクセスが即座に停止
                    '''
                },
                
                'improved_security': {
                    'controls': [
                        '統一されたパスワードポリシー',
                        '集中的な監査ログ',
                        'リアルタイムのアクセス制御',
                        'コンプライアンスの簡素化'
                    ]
                }
            },
            
            'business_benefits': {
                'cost_reduction': {
                    'areas': [
                        'ヘルプデスク対応の削減(最大40%)',
                        'パスワードリセットコストの削減',
                        'システム統合コストの削減',
                        '監査コストの削減'
                    ],
                    'roi': '通常6-12ヶ月で投資回収'
                },
                
                'agility': {
                    'capabilities': [
                        '新サービスの迅速な導入',
                        'M&A時の迅速な統合',
                        'パートナー連携の容易化',
                        'クラウドサービスの活用'
                    ]
                }
            }
        }

7.1.3 フェデレーション認証のアーキテクチャ

class FederationArchitecture:
    """フェデレーション認証のアーキテクチャ"""
    
    def architectural_patterns(self):
        """主要なアーキテクチャパターン"""
        
        return {
            'hub_and_spoke': {
                'description': 'ハブ&スポーク型',
                'diagram': '''
                           ┌─────────┐
                           │   IdP   │
                           │  (Hub)  │
                           └────┬────┘
                                │
                    ┌───────────┼───────────┐
                    │           │           │
                ┌───▼───┐  ┌───▼───┐  ┌───▼───┐
                │  SP1  │  │  SP2  │  │  SP3  │
                │(Spoke)│  │(Spoke)│  │(Spoke)│
                └───────┘  └───────┘  └───────┘
                ''',
                'characteristics': [
                    '単一のIdPがすべてのSPと信頼関係',
                    '管理が簡単',
                    'IdPが単一障害点になるリスク'
                ],
                'use_case': '企業内SSO'
            },
            
            'federated_network': {
                'description': 'フェデレーションネットワーク型',
                'diagram': '''
                ┌───────┐      ┌───────┐
                │ IdP-A │◀────▶│ IdP-B │
                └───┬───┘      └───┬───┘
                    │              │
                ┌───▼───┐      ┌───▼───┐
                │ SP-A1 │      │ SP-B1 │
                └───────┘      └───────┘
                
                相互信頼関係
                ''',
                'characteristics': [
                    '複数のIdP間で信頼関係',
                    '組織間連携に適している',
                    '複雑な信頼関係の管理'
                ],
                'use_case': '大学間連携、企業間連携'
            },
            
            'proxy_model': {
                'description': 'プロキシ型',
                'diagram': '''
                ┌────────┐    ┌─────────┐    ┌────────┐
                │External│───▶│  Proxy  │───▶│Internal│
                │  IdP   │    │   IdP   │    │   SP   │
                └────────┘    └─────────┘    └────────┘
                ''',
                'characteristics': [
                    '内部と外部の橋渡し',
                    'プロトコル変換が可能',
                    'セキュリティ境界の明確化'
                ],
                'use_case': 'B2B連携、クラウド統合'
            }
        }
    
    def trust_establishment(self):
        """信頼関係の確立方法"""
        
        return {
            'metadata_exchange': {
                'concept': 'メタデータの交換',
                'saml_example': '''
                <!-- IdPメタデータ -->
                <EntityDescriptor entityID="https://idp.example.com">
                    <IDPSSODescriptor>
                        <KeyDescriptor use="signing">
                            <KeyInfo>
                                <X509Data>
                                    <X509Certificate>
                                        MIIDXTCCAkWgAwIBAgIJAKl...
                                    </X509Certificate>
                                </X509Data>
                            </KeyInfo>
                        </KeyDescriptor>
                        <SingleSignOnService 
                            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                            Location="https://idp.example.com/sso"/>
                    </IDPSSODescriptor>
                </EntityDescriptor>
                ''',
                'oidc_example': '''
                // OpenID Connect Discovery
                GET /.well-known/openid-configuration
                
                {
                    "issuer": "https://idp.example.com",
                    "authorization_endpoint": "https://idp.example.com/auth",
                    "token_endpoint": "https://idp.example.com/token",
                    "jwks_uri": "https://idp.example.com/jwks",
                    "response_types_supported": ["code", "id_token"]
                }
                '''
            },
            
            'key_management': {
                'signing_keys': {
                    'purpose': 'アサーション/トークンの署名',
                    'rotation': '定期的な鍵のローテーション',
                    'distribution': 'JWKSエンドポイントまたはメタデータ'
                },
                'encryption_keys': {
                    'purpose': '機密情報の暗号化',
                    'algorithms': ['RSA-OAEP', 'AES-GCM'],
                    'key_agreement': '事前の鍵交換'
                }
            }
        }

7.2 OpenID Connectの仕組み - OAuthとの違いと追加価値

7.2.1 なぜOpenID Connectが生まれたのか

class OpenIDConnectRationale:
    """OpenID Connect誕生の背景"""
    
    def oauth2_limitations(self):
        """OAuth 2.0の限界"""
        
        return {
            'authentication_vs_authorization': {
                'oauth2_purpose': '認可のためのプロトコル',
                'common_misuse': '''
                # OAuth 2.0の誤用例
                
                # ❌ 間違い:OAuth 2.0を認証に使う
                def authenticate_with_oauth(access_token):
                    # アクセストークンがあるから認証済み?
                    # 誰のトークンかわからない!
                    user_info = api.get_user_info(access_token)
                    # このAPIコールが追加で必要
                    return user_info
                
                問題点:
                1. トークンの所有者が不明
                2. 認証時刻が不明
                3. 認証方法が不明
                4. APIコールのオーバーヘッド
                ''',
                'security_issues': [
                    'トークン置換攻撃',
                    '認証コンテキストの欠如',
                    '標準化されていないユーザー情報取得'
                ]
            },
            
            'lack_of_standardization': {
                'problem': '各社独自の実装',
                'examples': '''
                # Facebook
                GET /me?access_token=TOKEN
                Response: {id, name, email}
                
                # Google
                GET /oauth2/v1/userinfo?access_token=TOKEN
                Response: {id, email, verified_email, name}
                
                # GitHub
                GET /user
                Authorization: token TOKEN
                Response: {login, id, email, name}
                
                すべて異なるエンドポイント、形式、フィールド名
                ''',
                'developer_impact': '各プロバイダーごとに個別実装が必要'
            }
        }
    
    def openid_connect_solution(self):
        """OpenID Connectの解決策"""
        
        return {
            'identity_layer': {
                'concept': 'OAuth 2.0の上に認証レイヤーを追加',
                'architecture': '''
                ┌─────────────────────────────────┐
                │     OpenID Connect (OIDC)       │ ← 認証
                ├─────────────────────────────────┤
                │        OAuth 2.0                │ ← 認可
                ├─────────────────────────────────┤
                │         HTTP/TLS                │ ← 転送
                └─────────────────────────────────┘
                ''',
                'additions': [
                    'IDトークン(認証の証明)',
                    '標準化されたスコープとクレーム',
                    'UserInfoエンドポイント',
                    'ディスカバリメカニズム'
                ]
            },
            
            'id_token': {
                'purpose': '認証イベントの証明',
                'structure': '''
                {
                    // 必須クレーム
                    "iss": "https://idp.example.com",     // 発行者
                    "sub": "248289761001",                // サブジェクト(ユーザー)
                    "aud": "s6BhdRkqt3",                  // オーディエンス(クライアント)
                    "exp": 1311281970,                    // 有効期限
                    "iat": 1311280970,                    // 発行時刻
                    
                    // 認証関連クレーム
                    "auth_time": 1311280969,              // 認証時刻
                    "nonce": "n-0S6_WzA2Mj",             // リプレイ攻撃防止
                    "acr": "urn:mace:incommon:iap:silver", // 認証コンテキスト
                    "amr": ["pwd", "otp"],                // 認証方法
                    
                    // ユーザー情報
                    "name": "Jane Doe",
                    "email": "janedoe@example.com",
                    "email_verified": true
                }
                ''',
                'benefits': [
                    '認証の証明がトークン内に含まれる',
                    '追加のAPI呼び出し不要',
                    '改ざん防止(署名付き)',
                    '標準化された形式'
                ]
            }
        }

7.2.2 OpenID Connectのフロー

class OpenIDConnectFlows:
    """OpenID Connectの各種フロー"""
    
    def authorization_code_flow(self):
        """認可コードフロー(最も安全)"""
        
        return {
            'flow_diagram': '''
            End-User        RP (Client)         OP (IdP)
               │              │                    │
               │  1. Access   │                    │
               ├─────────────▶│                    │
               │              │                    │
               │              │ 2. AuthN Request   │
               │              ├───────────────────▶│
               │              │ (response_type=code)│
               │              │                    │
               │         3. Authenticate           │
               │◀──────────────────────────────────┤
               │                                   │
               │         4. Authorize              │
               ├──────────────────────────────────▶│
               │                                   │
               │              │ 5. AuthN Response  │
               │              │◀───────────────────┤
               │              │ (code)             │
               │              │                    │
               │              │ 6. Token Request   │
               │              ├───────────────────▶│
               │              │ (code + PKCE)      │
               │              │                    │
               │              │ 7. Token Response  │
               │              │◀───────────────────┤
               │              │ (ID Token +       │
               │              │  Access Token)     │
               │              │                    │
               │  8. Service  │                    │
               │◀─────────────┤                    │
            ''',
            
            'implementation': '''
            from authlib.integrations.flask_client import OAuth
            import secrets
            
            class OpenIDConnectClient:
                def __init__(self, app):
                    self.oauth = OAuth(app)
                    self.client = self.oauth.register(
                        name='oidc',
                        client_id='your-client-id',
                        client_secret='your-client-secret',
                        server_metadata_url='https://op.example.com/.well-known/openid-configuration',
                        client_kwargs={
                            'scope': 'openid profile email'
                        }
                    )
                
                def login(self):
                    """ログイン開始"""
                    # nonce生成(リプレイ攻撃対策)
                    nonce = secrets.token_urlsafe(32)
                    session['nonce'] = nonce
                    
                    redirect_uri = url_for('callback', _external=True)
                    return self.client.authorize_redirect(
                        redirect_uri,
                        nonce=nonce
                    )
                
                def callback(self):
                    """コールバック処理"""
                    # トークン取得
                    token = self.client.authorize_access_token()
                    
                    # IDトークンの検証
                    nonce = session.pop('nonce', None)
                    id_token = token.get('id_token')
                    
                    claims = self._verify_id_token(id_token, nonce)
                    
                    # ユーザー情報取得(必要な場合)
                    if 'userinfo_endpoint' in self.client.server_metadata:
                        user_info = self.client.get('userinfo').json()
                        claims.update(user_info)
                    
                    return claims
                
                def _verify_id_token(self, id_token, nonce):
                    """IDトークンの検証"""
                    # JWTの検証
                    claims = jwt.decode(
                        id_token,
                        self.client.jwks,
                        claims_options={
                            "iss": {"essential": True, "value": self.client.server_metadata['issuer']},
                            "aud": {"essential": True, "value": self.client.client_id},
                            "exp": {"essential": True},
                            "iat": {"essential": True}
                        }
                    )
                    
                    # nonce検証
                    if claims.get('nonce') != nonce:
                        raise SecurityError("Nonce mismatch")
                    
                    # 追加の検証
                    self._validate_timestamps(claims)
                    
                    return claims
                
                def _validate_timestamps(self, claims):
                    """タイムスタンプの検証"""
                    current_time = int(time.time())
                    
                    # 有効期限
                    if current_time >= claims['exp']:
                        raise TokenExpiredError("ID token expired")
                    
                    # 発行時刻(未来でないこと)
                    if claims['iat'] > current_time + 60:  # 1分の猶予
                        raise SecurityError("Token issued in the future")
                    
                    # 認証時刻(あまりに古くないこと)
                    if 'auth_time' in claims:
                        max_age = 3600  # 1時間
                        if current_time - claims['auth_time'] > max_age:
                            raise ReauthenticationRequired("Authentication too old")
            '''
        }
    
    def implicit_flow_deprecated(self):
        """暗黙的フロー(非推奨)"""
        
        return {
            'deprecation_notice': '''
            ⚠️ 重要:Implicit Flowは非推奨
            
            理由:
            1. トークンがURLフラグメントに露出
            2. ブラウザ履歴に残る可能性
            3. より安全な代替手段の存在
            
            代替:
            - SPAの場合:Authorization Code Flow + PKCE
            - ネイティブアプリ:Authorization Code Flow + PKCE
            ''',
            
            'migration_guide': '''
            # Implicit Flowからの移行
            
            # Before (Implicit)
            response_type=id_token token
            
            # After (Authorization Code + PKCE)
            response_type=code
            code_challenge=XXXXX
            code_challenge_method=S256
            '''
        }
    
    def hybrid_flow(self):
        """ハイブリッドフロー"""
        
        return {
            'use_case': '即座にIDトークンが必要な場合',
            'response_types': [
                'code id_token',
                'code token',
                'code id_token token'
            ],
            'benefits': [
                '認証の即座の確認',
                'フロントエンドでの早期処理',
                'バックエンドでの追加検証'
            ],
            'security_considerations': [
                'フロントチャネルでのトークン露出',
                '適切なnonce使用が必須'
            ]
        }

7.2.3 OpenID Connectの重要な概念

class OpenIDConnectConcepts:
    """OpenID Connectの重要な概念"""
    
    def claims_and_scopes(self):
        """クレームとスコープ"""
        
        return {
            'standard_scopes': {
                'openid': {
                    'required': True,
                    'purpose': 'OpenID Connectの使用を示す',
                    'claims': ['sub']
                },
                'profile': {
                    'claims': [
                        'name', 'family_name', 'given_name',
                        'middle_name', 'nickname', 'preferred_username',
                        'profile', 'picture', 'website',
                        'gender', 'birthdate', 'zoneinfo',
                        'locale', 'updated_at'
                    ]
                },
                'email': {
                    'claims': ['email', 'email_verified']
                },
                'address': {
                    'claims': ['address']
                },
                'phone': {
                    'claims': ['phone_number', 'phone_number_verified']
                }
            },
            
            'custom_claims': {
                'example': '''
                # カスタムクレームの定義
                {
                    "sub": "248289761001",
                    "name": "Jane Doe",
                    "email": "janedoe@example.com",
                    
                    // カスタムクレーム
                    "department": "Engineering",
                    "employee_id": "EMP001234",
                    "cost_center": "CC-789",
                    "manager": "john.smith@example.com",
                    "security_clearance": "confidential"
                }
                ''',
                'best_practices': [
                    '名前空間の使用(例:https://example.com/claims/)',
                    '必要最小限の情報のみ含める',
                    'PII(個人識別情報)の扱いに注意'
                ]
            },
            
            'claims_request': {
                'purpose': '必要なクレームを明示的に要求',
                'example': '''
                {
                    "id_token": {
                        "email": {"essential": true},
                        "email_verified": {"essential": true},
                        "department": null
                    },
                    "userinfo": {
                        "name": null,
                        "picture": null
                    }
                }
                '''
            }
        }
    
    def discovery_and_dynamic_registration(self):
        """ディスカバリと動的登録"""
        
        return {
            'discovery': {
                'endpoint': '/.well-known/openid-configuration',
                'purpose': 'OPの設定情報を自動取得',
                'example_response': '''
                {
                    "issuer": "https://op.example.com",
                    "authorization_endpoint": "https://op.example.com/authorize",
                    "token_endpoint": "https://op.example.com/token",
                    "userinfo_endpoint": "https://op.example.com/userinfo",
                    "jwks_uri": "https://op.example.com/jwks",
                    "registration_endpoint": "https://op.example.com/register",
                    
                    "scopes_supported": ["openid", "profile", "email"],
                    "response_types_supported": ["code", "code id_token"],
                    "grant_types_supported": ["authorization_code", "refresh_token"],
                    "subject_types_supported": ["public", "pairwise"],
                    "id_token_signing_alg_values_supported": ["RS256", "ES256"],
                    
                    "claims_supported": ["sub", "iss", "name", "email"],
                    "request_parameter_supported": true,
                    "request_uri_parameter_supported": false
                }
                ''',
                'benefits': [
                    '手動設定不要',
                    '設定変更への自動対応',
                    'エラーの削減'
                ]
            },
            
            'dynamic_registration': {
                'purpose': '実行時のクライアント登録',
                'flow': '''
                POST /register
                Content-Type: application/json
                
                {
                    "application_type": "web",
                    "redirect_uris": ["https://client.example.com/callback"],
                    "client_name": "Example Client",
                    "logo_uri": "https://client.example.com/logo.png",
                    "token_endpoint_auth_method": "client_secret_basic",
                    "grant_types": ["authorization_code", "refresh_token"],
                    "response_types": ["code"]
                }
                
                Response:
                {
                    "client_id": "s6BhdRkqt3",
                    "client_secret": "ZJYCqe3GGRvdrudKyZS0XhGv_Z45DuKhCUk0gBR1vZk",
                    "registration_access_token": "this.is.an.access.token.value.ffx83",
                    "registration_client_uri": "https://op.example.com/register/s6BhdRkqt3",
                    "client_secret_expires_at": 1577858400
                }
                ''',
                'use_cases': [
                    'マルチテナントSaaS',
                    '開発者ポータル',
                    '自動プロビジョニング'
                ]
            }
        }
    
    def advanced_features(self):
        """高度な機能"""
        
        return {
            'request_object': {
                'purpose': 'リクエストパラメータのJWT化',
                'benefits': [
                    'パラメータの改ざん防止',
                    '機密情報の暗号化',
                    'リクエストの否認防止'
                ],
                'example': '''
                // リクエストオブジェクトの作成
                const requestObject = jwt.sign({
                    iss: "s6BhdRkqt3",
                    aud: "https://op.example.com",
                    response_type: "code",
                    client_id: "s6BhdRkqt3",
                    redirect_uri: "https://client.example.com/cb",
                    scope: "openid profile",
                    state: "af0ifjsldkj",
                    nonce: "n-0S6_WzA2Mj",
                    max_age: 86400,
                    claims: {
                        id_token: {
                            auth_time: { essential: true }
                        }
                    }
                }, clientPrivateKey, { algorithm: 'RS256' });
                
                // 認可リクエスト
                https://op.example.com/authorize?request=${requestObject}
                '''
            },
            
            'session_management': {
                'purpose': 'SSOセッションの管理',
                'mechanisms': [
                    'check_session_iframe',
                    'end_session_endpoint',
                    'front/back-channel logout'
                ],
                'implementation': '''
                // セッション監視
                window.addEventListener("message", (e) => {
                    if (e.origin !== "https://op.example.com") return;
                    
                    if (e.data === "changed") {
                        // セッション状態が変更された
                        // サイレント再認証または再ログイン
                        checkSessionStatus();
                    }
                });
                '''
            }
        }

7.3 SAMLとの比較 - それぞれの適用領域

7.3.1 SAMLの概要と特徴

class SAMLOverview:
    """SAML 2.0の概要"""
    
    def saml_fundamentals(self):
        """SAMLの基本"""
        
        return {
            'what_is_saml': {
                'full_name': 'Security Assertion Markup Language',
                'version': '2.0 (2005年から標準)',
                'format': 'XML-based',
                'purpose': 'セキュリティアサーションの交換'
            },
            
            'core_components': {
                'assertions': {
                    'description': 'セキュリティ情報の声明',
                    'types': [
                        'Authentication Assertion(認証アサーション)',
                        'Attribute Assertion(属性アサーション)',
                        'Authorization Decision Assertion(認可決定アサーション)'
                    ],
                    'example': '''
                    <saml:Assertion ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6"
                                    Version="2.0"
                                    IssueInstant="2024-01-04T05:00:00Z">
                        <saml:Issuer>https://idp.example.com</saml:Issuer>
                        
                        <saml:Subject>
                            <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
                                jdoe@example.com
                            </saml:NameID>
                        </saml:Subject>
                        
                        <saml:AuthnStatement AuthnInstant="2024-01-04T05:00:00Z">
                            <saml:AuthnContext>
                                <saml:AuthnContextClassRef>
                                    urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
                                </saml:AuthnContextClassRef>
                            </saml:AuthnContext>
                        </saml:AuthnStatement>
                        
                        <saml:AttributeStatement>
                            <saml:Attribute Name="email">
                                <saml:AttributeValue>jdoe@example.com</saml:AttributeValue>
                            </saml:Attribute>
                            <saml:Attribute Name="department">
                                <saml:AttributeValue>Engineering</saml:AttributeValue>
                            </saml:Attribute>
                        </saml:AttributeStatement>
                    </saml:Assertion>
                    '''
                },
                
                'protocols': {
                    'description': 'リクエスト/レスポンスのルール',
                    'main_protocols': [
                        'Authentication Request Protocol',
                        'Single Logout Protocol',
                        'Artifact Resolution Protocol',
                        'Name Identifier Management Protocol'
                    ]
                },
                
                'bindings': {
                    'description': 'プロトコルメッセージの転送方法',
                    'types': [
                        'HTTP Redirect Binding',
                        'HTTP POST Binding',
                        'HTTP Artifact Binding',
                        'SOAP Binding'
                    ]
                }
            }
        }
    
    def saml_flow(self):
        """SAML SSOフロー"""
        
        return {
            'sp_initiated_sso': {
                'description': 'SP起動のSSO',
                'flow': '''
                User         SP              IdP
                 │           │               │
                 │ 1.Access  │               │
                 ├──────────▶│               │
                 │           │               │
                 │           │ 2.AuthnRequest│
                 │           ├──────────────▶│
                 │           │               │
                 │      3.Login Form         │
                 │◀──────────────────────────┤
                 │                           │
                 │      4.Credentials        │
                 ├──────────────────────────▶│
                 │                           │
                 │           │ 5.SAMLResponse│
                 │           │◀──────────────┤
                 │           │ (Assertion)   │
                 │           │               │
                 │ 6.Service │               │
                 │◀──────────┤               │
                ''',
                
                'implementation': '''
                from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
                from saml2.client import Saml2Client
                from saml2.config import Config as Saml2Config
                
                class SAMLServiceProvider:
                    def __init__(self):
                        self.saml_client = Saml2Client(config=self._get_saml_config())
                    
                    def _get_saml_config(self):
                        return {
                            'entityid': 'https://sp.example.com',
                            'metadata': {
                                'local': ['idp_metadata.xml']
                            },
                            'service': {
                                'sp': {
                                    'endpoints': {
                                        'assertion_consumer_service': [
                                            ('https://sp.example.com/saml/acs', BINDING_HTTP_POST)
                                        ]
                                    },
                                    'authn_requests_signed': True,
                                    'want_assertions_signed': True,
                                }
                            },
                            'key_file': 'sp_key.pem',
                            'cert_file': 'sp_cert.pem'
                        }
                    
                    def initiate_sso(self):
                        """SSO開始"""
                        session_id, auth_request = self.saml_client.prepare_for_authenticate()
                        
                        # セッションIDを保存(後で使用)
                        session['saml_session_id'] = session_id
                        
                        # IdPへリダイレクト
                        redirect_url = auth_request
                        return redirect(redirect_url)
                    
                    def handle_sso_response(self, saml_response):
                        """SAML Responseの処理"""
                        # レスポンスの検証
                        authn_response = self.saml_client.parse_authn_request_response(
                            saml_response,
                            BINDING_HTTP_POST
                        )
                        
                        # アサーションの検証
                        if not self._validate_response(authn_response):
                            raise SecurityError("Invalid SAML response")
                        
                        # ユーザー情報の抽出
                        user_info = {
                            'nameid': authn_response.name_id.text,
                            'attributes': authn_response.ava,  # Attribute Value Assertions
                            'session_index': authn_response.session_index
                        }
                        
                        return user_info
                    
                    def _validate_response(self, response):
                        """レスポンスの検証"""
                        # 署名検証
                        if not response.is_signed():
                            return False
                        
                        # 有効期限検証
                        if response.not_on_or_after and response.not_on_or_after < time.time():
                            return False
                        
                        # Audienceの検証
                        if self.saml_client.config.entityid not in response.assertion.audience:
                            return False
                        
                        return True
                '''
            }
        }

7.3.2 OpenID ConnectとSAMLの詳細比較

class OIDCvsSAMLComparison:
    """OpenID ConnectとSAMLの比較"""
    
    def technical_comparison(self):
        """技術的な比較"""
        
        return {
            'data_format': {
                'oidc': {
                    'format': 'JSON/JWT',
                    'example': '''
                    {
                        "iss": "https://idp.example.com",
                        "sub": "248289761001",
                        "aud": "s6BhdRkqt3",
                        "exp": 1311281970,
                        "name": "Jane Doe",
                        "email": "jane@example.com"
                    }
                    ''',
                    'size': '~1KB',
                    'parsing': 'JSONパーサー(すべての言語で利用可能)'
                },
                'saml': {
                    'format': 'XML',
                    'example': '(前述のXMLアサーション)',
                    'size': '~10KB',
                    'parsing': 'XMLパーサー、署名検証ライブラリが必要'
                }
            },
            
            'transport': {
                'oidc': {
                    'primary': 'HTTPS REST API',
                    'bindings': ['フロントチャネル', 'バックチャネル'],
                    'simplicity': 'シンプル(HTTPのみ)'
                },
                'saml': {
                    'primary': 'HTTP POST/Redirect, SOAP',
                    'bindings': ['複数のバインディング'],
                    'complexity': '複雑(用途に応じて選択)'
                }
            },
            
            'security_model': {
                'oidc': {
                    'token_validation': 'JWT署名検証',
                    'encryption': 'JWE(オプション)',
                    'key_distribution': 'JWKS endpoint'
                },
                'saml': {
                    'assertion_validation': 'XML署名',
                    'encryption': 'XML暗号化',
                    'key_distribution': 'メタデータ交換'
                }
            },
            
            'implementation_complexity': {
                'oidc': {
                    'learning_curve': '低〜中',
                    'library_support': '豊富',
                    'debugging': '容易(JSON形式)',
                    'common_issues': [
                        'トークン有効期限',
                        'CORS設定',
                        'Nonceの扱い'
                    ]
                },
                'saml': {
                    'learning_curve': '高',
                    'library_support': '成熟しているが限定的',
                    'debugging': '困難(XML署名)',
                    'common_issues': [
                        'XML署名の検証',
                        '時刻同期',
                        'メタデータ管理'
                    ]
                }
            }
        }
    
    def use_case_comparison(self):
        """ユースケース別の比較"""
        
        return {
            'enterprise_sso': {
                'scenario': '大企業の内部SSO',
                'recommendation': 'SAML',
                'reasons': [
                    '既存のSAML対応製品が多い',
                    'IT部門がXML/SAMLに精通',
                    '詳細な認証コンテキスト',
                    '複雑な属性マッピング'
                ],
                'example_products': ['AD FS', 'Okta', 'Ping Identity']
            },
            
            'mobile_apps': {
                'scenario': 'モバイルアプリの認証',
                'recommendation': 'OpenID Connect',
                'reasons': [
                    'RESTful APIとの親和性',
                    'コンパクトなトークン',
                    'ネイティブアプリサポート',
                    'PKCE対応'
                ]
            },
            
            'b2c_services': {
                'scenario': 'コンシューマー向けサービス',
                'recommendation': 'OpenID Connect',
                'reasons': [
                    'ソーシャルログイン統合',
                    '開発者フレンドリー',
                    'JavaScriptでの実装容易性',
                    'モダンなWeb標準'
                ]
            },
            
            'legacy_integration': {
                'scenario': 'レガシーシステム統合',
                'recommendation': 'SAML',
                'reasons': [
                    '2005年から標準',
                    '多くのレガシー製品が対応',
                    'SOAP Webサービスとの統合'
                ]
            },
            
            'microservices': {
                'scenario': 'マイクロサービスアーキテクチャ',
                'recommendation': 'OpenID Connect',
                'reasons': [
                    'JWT(ステートレス)',
                    'API Gateway統合',
                    'サービスメッシュ対応',
                    'トークンイントロスペクション'
                ]
            }
        }
    
    def migration_considerations(self):
        """移行に関する考慮事項"""
        
        return {
            'saml_to_oidc': {
                'drivers': [
                    'モバイル/SPA対応の必要性',
                    'RESTful API採用',
                    '開発効率の向上',
                    'クラウドネイティブ化'
                ],
                'challenges': [
                    '属性マッピングの違い',
                    'セッション管理の違い',
                    '既存統合の書き換え'
                ],
                'migration_pattern': '''
                # 段階的移行パターン
                
                Phase 1: デュアルプロトコルサポート
                ├── 新規アプリ → OpenID Connect
                └── 既存アプリ → SAML維持
                
                Phase 2: プロキシ経由の移行
                ├── SAML → Proxy → OIDC変換
                └── 透過的な移行
                
                Phase 3: 完全移行
                └── すべてOIDCに統一
                '''
            },
            
            'coexistence_strategy': {
                'approach': 'ハイブリッドIdP',
                'implementation': '''
                class HybridIdentityProvider:
                    """SAMLとOIDCの両方をサポート"""
                    
                    def authenticate(self, request):
                        # 共通の認証処理
                        user = self._perform_authentication(request)
                        
                        # プロトコルに応じた応答
                        if request.is_saml():
                            return self._create_saml_response(user)
                        elif request.is_oidc():
                            return self._create_oidc_response(user)
                    
                    def _create_saml_response(self, user):
                        # SAML Assertion生成
                        pass
                    
                    def _create_oidc_response(self, user):
                        # ID Token生成
                        pass
                '''
            }
        }

7.4 エンタープライズでの活用 - 実際の導入事例と課題

7.4.1 エンタープライズSSO実装パターン

class EnterpriseSSOPatterns:
    """エンタープライズSSO実装パターン"""
    
    def implementation_patterns(self):
        """実装パターン"""
        
        return {
            'centralized_idp': {
                'pattern': '中央集権型IdP',
                'architecture': '''
                ┌─────────────────────────────────────┐
                │       Enterprise IdP (Okta等)       │
                │  ┌─────────┬─────────┬─────────┐  │
                │  │  LDAP   │  MFA    │  SIEM   │  │
                │  └─────────┴─────────┴─────────┘  │
                └───────────┬─────────────────────────┘
                            │
                ┌───────────┼───────────┬─────────────┐
                │           │           │             │
            ┌───▼───┐  ┌───▼───┐  ┌───▼───┐  ┌─────▼─────┐
            │Office │  │  CRM  │  │  ERP  │  │External   │
            │  365  │  │System │  │System │  │SaaS Apps  │
            └───────┘  └───────┘  └───────┘  └───────────┘
                ''',
                'benefits': [
                    '単一の管理ポイント',
                    '統一されたセキュリティポリシー',
                    '包括的な監査ログ'
                ],
                'challenges': [
                    '単一障害点',
                    'ベンダーロックイン',
                    'カスタマイズの制限'
                ]
            },
            
            'federated_multi_idp': {
                'pattern': 'フェデレーション型マルチIdP',
                'use_case': 'M&A、グローバル企業',
                'implementation': '''
                class FederatedSSOManager:
                    def __init__(self):
                        self.idp_registry = {
                            'corp_hq': {
                                'type': 'saml',
                                'metadata_url': 'https://hq-idp.corp.com/metadata'
                            },
                            'subsidiary_a': {
                                'type': 'oidc',
                                'discovery_url': 'https://idp.subsidiary-a.com/.well-known'
                            },
                            'partner_b': {
                                'type': 'saml',
                                'metadata_url': 'https://partner-b.com/saml/metadata'
                            }
                        }
                    
                    def route_authentication(self, email):
                        """メールドメインに基づいてIdPを選択"""
                        domain = email.split('@')[1]
                        
                        idp_mapping = {
                            'corp.com': 'corp_hq',
                            'subsidiary-a.com': 'subsidiary_a',
                            'partner-b.com': 'partner_b'
                        }
                        
                        idp_id = idp_mapping.get(domain)
                        if not idp_id:
                            raise UnknownDomainError(f"No IdP for domain: {domain}")
                        
                        return self._initiate_sso(idp_id)
                    
                    def establish_trust(self, idp_config):
                        """IdP間の信頼関係確立"""
                        if idp_config['type'] == 'saml':
                            return self._setup_saml_trust(idp_config)
                        elif idp_config['type'] == 'oidc':
                            return self._setup_oidc_trust(idp_config)
                '''
            }
        }
    
    def real_world_case_studies(self):
        """実際の導入事例"""
        
        return {
            'global_manufacturer': {
                'company_profile': {
                    'industry': '製造業',
                    'employees': '50,000+',
                    'countries': '30+',
                    'systems': '200+'
                },
                'challenges': [
                    '各国の規制対応',
                    'レガシーシステムの統合',
                    'M&Aによる異なるIT環境'
                ],
                'solution': {
                    'architecture': 'ハイブリッド(SAML + OIDC)',
                    'implementation': '''
                    Phase 1: 地域別IdPの構築
                    - EMEA: AD FS (SAML)
                    - Americas: Okta (SAML/OIDC)
                    - APAC: Azure AD (SAML/OIDC)
                    
                    Phase 2: フェデレーション確立
                    - 地域間の信頼関係
                    - 属性マッピングの標準化
                    
                    Phase 3: アプリケーション移行
                    - 優先度に基づく段階的移行
                    - レガシーアプリのプロキシ経由統合
                    ''',
                    'results': {
                        'password_reset_reduction': '75%',
                        'onboarding_time': '2日 → 2時間',
                        'security_incidents': '60%削減'
                    }
                }
            },
            
            'financial_services': {
                'company_profile': {
                    'industry': '金融サービス',
                    'employees': '20,000+',
                    'regulations': ['PCI-DSS', 'SOX', 'GDPR'],
                    'security_requirements': 'HIGHEST'
                },
                'implementation': {
                    'special_requirements': [
                        'ステップアップ認証',
                        'コンテキストベース認証',
                        'セッション管理の厳格化'
                    ],
                    'technical_solution': '''
                    class RiskBasedAuthentication:
                        def evaluate_auth_requirements(self, context):
                            risk_score = self.calculate_risk_score(context)
                            
                            if risk_score < 30:
                                return {'method': 'password', 'session_lifetime': 8*3600}
                            elif risk_score < 70:
                                return {'method': 'password+otp', 'session_lifetime': 4*3600}
                            else:
                                return {'method': 'password+otp+biometric', 'session_lifetime': 1*3600}
                        
                        def calculate_risk_score(self, context):
                            score = 0
                            
                            # デバイスの信頼性
                            if not context.is_managed_device:
                                score += 30
                            
                            # アクセス場所
                            if context.is_unusual_location:
                                score += 40
                            
                            # アクセス対象
                            if context.resource_sensitivity == 'high':
                                score += 30
                            
                            return score
                    '''
                }
            }
        }
    
    def common_challenges_and_solutions(self):
        """共通の課題と解決策"""
        
        return {
            'legacy_system_integration': {
                'challenge': 'レガシーシステムがSAML/OIDC非対応',
                'solutions': [
                    {
                        'approach': 'リバースプロキシ',
                        'implementation': '''
                        # Nginxでのヘッダーインジェクション
                        location /legacy-app {
                            auth_request /auth/verify;
                            auth_request_set $user $upstream_http_x_user;
                            auth_request_set $email $upstream_http_x_email;
                            
                            proxy_set_header X-User $user;
                            proxy_set_header X-Email $email;
                            proxy_pass http://legacy-app-server;
                        }
                        '''
                    },
                    {
                        'approach': 'エージェントベース',
                        'description': 'アプリケーションサーバーにエージェント導入'
                    }
                ]
            },
            
            'attribute_mapping': {
                'challenge': '異なるシステム間での属性の不一致',
                'solution': '''
                class AttributeMapper:
                    def __init__(self):
                        self.mappings = {
                            'saml_to_internal': {
                                'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'email',
                                'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'full_name',
                                'Department': 'department',
                                'EmployeeID': 'employee_id'
                            },
                            'oidc_to_internal': {
                                'email': 'email',
                                'name': 'full_name',
                                'department': 'department',
                                'employee_id': 'employee_id'
                            }
                        }
                    
                    def map_attributes(self, source_attributes, source_type):
                        mapping = self.mappings.get(f'{source_type}_to_internal', {})
                        
                        internal_attributes = {}
                        for source_key, source_value in source_attributes.items():
                            internal_key = mapping.get(source_key, source_key)
                            internal_attributes[internal_key] = source_value
                        
                        return internal_attributes
                    
                    def validate_required_attributes(self, attributes):
                        required = ['email', 'employee_id']
                        missing = [attr for attr in required if attr not in attributes]
                        
                        if missing:
                            raise AttributeMissingError(f"Missing required attributes: {missing}")
                '''
            },
            
            'session_management': {
                'challenge': 'グローバルログアウトの実装',
                'approaches': {
                    'front_channel_logout': {
                        'description': 'ブラウザ経由でのログアウト通知',
                        'pros': 'シンプル',
                        'cons': 'ブラウザ依存、信頼性低い'
                    },
                    'back_channel_logout': {
                        'description': 'サーバー間でのログアウト通知',
                        'pros': '信頼性高い',
                        'cons': '実装複雑',
                        'implementation': '''
                        async def handle_backchannel_logout(logout_token):
                            # ログアウトトークンの検証
                            claims = validate_logout_token(logout_token)
                            
                            # 該当セッションの特定
                            session_id = claims.get('sid')
                            user_id = claims.get('sub')
                            
                            # セッションの無効化
                            if session_id:
                                await invalidate_session(session_id)
                            elif user_id:
                                await invalidate_all_user_sessions(user_id)
                            
                            # 他のサービスへの伝播
                            await propagate_logout(user_id)
                        '''
                    }
                }
            }
        }

7.4.2 セキュリティとコンプライアンス

class SSOSecurityCompliance:
    """SSOのセキュリティとコンプライアンス"""
    
    def security_best_practices(self):
        """セキュリティベストプラクティス"""
        
        return {
            'token_security': {
                'signing': {
                    'algorithms': ['RS256', 'ES256'],
                    'key_management': '''
                    class KeyRotationManager:
                        def __init__(self):
                            self.rotation_interval = 90 * 24 * 3600  # 90日
                            self.keys = self._load_keys()
                        
                        def get_current_key(self):
                            """現在の署名鍵を取得"""
                            return self.keys['current']
                        
                        def rotate_keys(self):
                            """鍵のローテーション"""
                            # 新しい鍵を生成
                            new_key = self._generate_key_pair()
                            
                            # 鍵の更新
                            self.keys = {
                                'current': new_key,
                                'previous': self.keys['current'],
                                'next_rotation': time.time() + self.rotation_interval
                            }
                            
                            # JWKSエンドポイントの更新
                            self._update_jwks()
                            
                            # 監査ログ
                            audit_log.info("Key rotation completed", {
                                'key_id': new_key['kid'],
                                'algorithm': new_key['alg']
                            })
                    '''
                },
                
                'encryption': {
                    'when_needed': [
                        'PII(個人識別情報)を含む場合',
                        'ネットワーク境界を越える場合',
                        '規制要件がある場合'
                    ],
                    'implementation': 'JWE (JSON Web Encryption)'
                }
            },
            
            'authentication_assurance': {
                'levels': {
                    'aal1': {
                        'description': '単一要素認証',
                        'methods': ['password'],
                        'use_case': '低リスクアクセス'
                    },
                    'aal2': {
                        'description': '多要素認証',
                        'methods': ['password+otp', 'password+push'],
                        'use_case': '中リスクアクセス'
                    },
                    'aal3': {
                        'description': 'ハードウェアベース認証',
                        'methods': ['fido2', 'smartcard'],
                        'use_case': '高リスクアクセス'
                    }
                },
                
                'implementation': '''
                def determine_required_aal(resource, user_context):
                    """必要な認証保証レベルを決定"""
                    
                    # リソースの機密性
                    resource_sensitivity = classify_resource(resource)
                    
                    # ユーザーコンテキスト
                    risk_factors = assess_risk(user_context)
                    
                    # AAL決定ロジック
                    if resource_sensitivity == 'high' or risk_factors['score'] > 70:
                        return 'aal3'
                    elif resource_sensitivity == 'medium' or risk_factors['score'] > 40:
                        return 'aal2'
                    else:
                        return 'aal1'
                '''
            },
            
            'audit_and_monitoring': {
                'required_events': [
                    'authentication_success',
                    'authentication_failure',
                    'token_issued',
                    'token_refreshed',
                    'logout',
                    'authorization_change'
                ],
                
                'implementation': '''
                class SSOAuditLogger:
                    def __init__(self):
                        self.logger = self._setup_logger()
                        self.siem_client = SIEMClient()
                    
                    def log_authentication_event(self, event_type, context):
                        """認証イベントのログ"""
                        
                        event = {
                            'timestamp': datetime.utcnow().isoformat(),
                            'event_type': event_type,
                            'user_id': context.get('user_id'),
                            'ip_address': context.get('ip_address'),
                            'user_agent': context.get('user_agent'),
                            'authentication_method': context.get('auth_method'),
                            'result': context.get('result'),
                            'risk_score': context.get('risk_score'),
                            'session_id': context.get('session_id')
                        }
                        
                        # ローカルログ
                        self.logger.info(json.dumps(event))
                        
                        # SIEM転送
                        self.siem_client.send_event(event)
                        
                        # アラート条件チェック
                        self._check_alert_conditions(event)
                    
                    def _check_alert_conditions(self, event):
                        """アラート条件のチェック"""
                        
                        # 連続した認証失敗
                        if event['event_type'] == 'authentication_failure':
                            failure_count = self._get_recent_failures(
                                event['user_id'], 
                                window=300  # 5分
                            )
                            
                            if failure_count >= 5:
                                self._trigger_alert('excessive_auth_failures', event)
                        
                        # 異常なアクセスパターン
                        if event.get('risk_score', 0) > 80:
                            self._trigger_alert('high_risk_access', event)
                '''
            }
        }
    
    def compliance_requirements(self):
        """コンプライアンス要件"""
        
        return {
            'gdpr': {
                'requirements': [
                    'データ最小化原則',
                    '目的外利用の禁止',
                    'データポータビリティ',
                    '忘れられる権利'
                ],
                'implementation': '''
                class GDPRCompliantSSO:
                    def minimize_data_collection(self):
                        """データ最小化"""
                        # 必要最小限の属性のみ要求
                        return {
                            'required_claims': ['sub', 'email'],
                            'optional_claims': ['name'],
                            'prohibited_claims': ['gender', 'birthdate']  # 業務上不要
                        }
                    
                    def implement_consent_management(self):
                        """同意管理"""
                        return {
                            'consent_required_for': [
                                'marketing_communications',
                                'data_analytics',
                                'third_party_sharing'
                            ],
                            'ui_implementation': 'Granular consent checkboxes',
                            'storage': 'Consent records with timestamps'
                        }
                    
                    def handle_data_requests(self, request_type, user_id):
                        """データリクエストの処理"""
                        if request_type == 'access':
                            return self.export_user_data(user_id)
                        elif request_type == 'deletion':
                            return self.delete_user_data(user_id)
                        elif request_type == 'portability':
                            return self.export_portable_data(user_id)
                '''
            },
            
            'sox': {
                'requirements': [
                    'アクセス制御の文書化',
                    '職務分離',
                    '変更管理',
                    '監査証跡'
                ],
                'controls': {
                    'access_certification': '四半期ごとのアクセス権棚卸し',
                    'privileged_access': '特権アクセスの追加監視',
                    'change_control': 'IdP設定変更の承認プロセス'
                }
            }
        }

まとめ

この章では、エンタープライズ環境でのSSO実現に不可欠な技術を学びました:

  1. フェデレーション認証の概念
    • 組織間連携の必要性
    • 信頼関係の確立
    • 中央集権vs分散モデル
  2. OpenID Connectの仕組み
    • OAuth 2.0との違い
    • IDトークンの役割
    • 各種フローと実装
  3. SAMLとの比較
    • 技術的な違い
    • それぞれの適用領域
    • 移行・共存戦略
  4. エンタープライズでの活用
    • 実装パターン
    • 実際の導入事例
    • セキュリティとコンプライアンス

次章では、これらの知識を基に、実際の認証システムの設計に入ります。

演習問題

問題1:OpenID Connect実装

以下の要件を満たすOpenID Connect RPを実装しなさい:

  • ディスカバリによる自動設定
  • IDトークンの完全な検証
  • UserInfoエンドポイントの利用
  • セッション管理

問題2:SAML統合

既存のWebアプリケーションにSAML SPを統合する設計を作成しなさい:

  • メタデータ管理
  • 属性マッピング
  • シングルログアウト対応

問題3:プロトコル選択

以下のシナリオに対して、OpenID ConnectとSAMLのどちらを選択すべきか、理由とともに説明しなさい:

  • 大学間の学術リソース共有
  • スタートアップのSaaS統合
  • 銀行のモバイルアプリ
  • 製造業のサプライチェーン連携

問題4:ハイブリッド実装

SAMLとOpenID Connectの両方をサポートするIdPの設計を作成しなさい:

  • 共通認証基盤
  • プロトコル変換
  • 属性の相互マッピング

問題5:セキュリティ監査

提供されたSSO実装に対してセキュリティ監査を実施し、改善提案を作成しなさい。