Chapter 09: Federated Identity and Single Sign-On

Learn SAML federation, OIDC, AWS SSO configuration, and enterprise identity management integration

Haiyue
49min

Chapter 9: Federated Identity and Single Sign-On

Learning Objectives

  1. Understand federated identity concepts and architecture
  2. Configure SAML 2.0 identity federation
  3. Implement OpenID Connect (OIDC) integration
  4. Master AWS SSO (Identity Center) configuration
  5. Establish enterprise-level single sign-on solutions

Federated Identity Architecture Diagram

🔄 正在渲染 Mermaid 图表...

9.1 Federated Identity Basics

9.1.1 Federated Identity Concepts and Architecture

import boto3
import json
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta
import base64
import urllib.parse

def understand_identity_federation():
    """
    Understanding federated identity concepts and architecture
    """

    federation_concepts = {
        "identity_federation": {
            "definition": "Identity federation allows users from external identity providers to access AWS resources without creating IAM users in AWS",
            "benefits": [
                "Single sign-on experience",
                "Centralized identity management",
                "Reduced password management complexity",
                "Support for enterprise-level identity providers",
                "Temporary credentials enhance security"
            ],
            "key_components": [
                "Identity Provider (IdP)",
                "Service Provider (SP) - AWS",
                "Identity Assertion",
                "Trust Relationship",
                "Role Mapping"
            ]
        },
        "federation_types": {
            "saml_federation": {
                "description": "Enterprise-level federation based on SAML 2.0 protocol",
                "use_cases": ["Enterprise Active Directory", "ADFS", "Third-party IdP"],
                "workflow": [
                    "User authenticates at enterprise IdP",
                    "IdP generates SAML assertion",
                    "User redirects to AWS SAML endpoint",
                    "AWS validates assertion and provides temporary credentials",
                    "User accesses AWS resources with temporary credentials"
                ]
            },
            "oidc_federation": {
                "description": "Web identity federation based on OpenID Connect",
                "use_cases": ["Mobile apps", "Web applications", "Third-party login"],
                "workflow": [
                    "User authenticates through OIDC provider",
                    "Obtain identity token",
                    "Call AWS STS with token",
                    "Obtain temporary AWS credentials",
                    "Access AWS resources"
                ]
            },
            "web_identity_federation": {
                "description": "Direct integration with web identity providers",
                "use_cases": ["Amazon Cognito", "Google", "Facebook login"],
                "workflow": [
                    "User logs in through Web IdP",
                    "Obtain identity token",
                    "Directly use AssumeRoleWithWebIdentity",
                    "Obtain temporary AWS credentials"
                ]
            }
        }
    }

    # Trust model for federated identity
    trust_model = {
        "trust_establishment": {
            "identity_provider_setup": "Configure identity provider in AWS",
            "trust_policy_creation": "Create trust policy to allow IdP to assume roles",
            "attribute_mapping": "Map IdP attributes to AWS roles",
            "condition_validation": "Validate federation conditions and constraints"
        },
        "security_considerations": [
            "Validate integrity and authenticity of SAML assertions",
            "Use appropriate conditions to restrict role access",
            "Regularly rotate encryption keys",
            "Monitor and audit federated access",
            "Implement least privilege principle"
        ]
    }

    print("📋 Federated Identity Concepts:")
    print(f"Definition: {federation_concepts['identity_federation']['definition']}")

    print("\nBenefits:")
    for benefit in federation_concepts['identity_federation']['benefits']:
        print(f"  • {benefit}")

    print("\nKey Components:")
    for component in federation_concepts['identity_federation']['key_components']:
        print(f"  • {component}")

    print("\n📋 Federation Types:")
    for fed_type, details in federation_concepts['federation_types'].items():
        print(f"\n{fed_type.replace('_', ' ').title()}:")
        print(f"  Description: {details['description']}")
        print(f"  Use Cases: {', '.join(details['use_cases'])}")

    return federation_concepts, trust_model

class IdentityFederationManager:
    """Identity Federation Manager"""

    def __init__(self):
        self.iam = boto3.client('iam')
        self.sts = boto3.client('sts')

    def create_saml_identity_provider(self, provider_name, metadata_document):
        """Create SAML identity provider"""
        try:
            response = self.iam.create_saml_provider(
                SAMLMetadataDocument=metadata_document,
                Name=provider_name,
                Tags=[
                    {'Key': 'Type', 'Value': 'SAML'},
                    {'Key': 'Purpose', 'Value': 'Federation'},
                    {'Key': 'CreatedDate', 'Value': datetime.now().strftime('%Y-%m-%d')}
                ]
            )

            provider_arn = response['SAMLProviderArn']
            print(f"✅ SAML identity provider created successfully: {provider_arn}")

            return provider_arn

        except Exception as e:
            print(f"❌ Failed to create SAML identity provider: {str(e)}")
            return None

    def create_oidc_identity_provider(self, provider_url, client_ids, thumbprints):
        """Create OIDC identity provider"""
        try:
            response = self.iam.create_open_id_connect_provider(
                Url=provider_url,
                ClientIDList=client_ids,
                ThumbprintList=thumbprints,
                Tags=[
                    {'Key': 'Type', 'Value': 'OIDC'},
                    {'Key': 'Purpose', 'Value': 'WebIdentityFederation'}
                ]
            )

            provider_arn = response['OpenIDConnectProviderArn']
            print(f"✅ OIDC identity provider created successfully: {provider_arn}")

            return provider_arn

        except Exception as e:
            print(f"❌ Failed to create OIDC identity provider: {str(e)}")
            return None

# Demonstrate federated identity concepts
federation_concepts, trust_model = understand_identity_federation()
federation_manager = IdentityFederationManager()

print("\n📋 Trust Model Components:")
for component, description in trust_model['trust_establishment'].items():
    print(f"  {component.replace('_', ' ').title()}: {description}")

9.2 SAML 2.0 Federation Configuration

9.2.1 Enterprise-Level SAML Integration

def configure_saml_federation():
    """
    Configure SAML 2.0 federated identity
    """

    class SAMLFederationManager:
        def __init__(self):
            self.iam = boto3.client('iam')
            self.sts = boto3.client('sts')

        def setup_enterprise_saml_federation(self, config):
            """Setup enterprise SAML federation"""

            # 1. Create SAML identity provider
            provider_arn = self._create_saml_provider(config)
            if not provider_arn:
                return None

            # 2. Create federated roles
            federation_roles = self._create_federation_roles(provider_arn, config)

            # 3. Configure attribute mapping
            attribute_mapping = self._setup_attribute_mapping(config)

            # 4. Generate IdP configuration guide
            idp_config = self._generate_idp_configuration(provider_arn, federation_roles)

            return {
                'provider_arn': provider_arn,
                'federation_roles': federation_roles,
                'attribute_mapping': attribute_mapping,
                'idp_configuration': idp_config
            }

        def _create_saml_provider(self, config):
            """Create SAML identity provider"""

            # Example SAML metadata (simplified version)
            metadata_template = f'''<?xml version="1.0" encoding="UTF-8"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
                     entityID="{config['entity_id']}">
    <md:IDPSSODescriptor WantAuthnRequestsSigned="false"
                         protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <md:KeyDescriptor use="signing">
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:X509Data>
                    <ds:X509Certificate>
                        {config['signing_certificate']}
                    </ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </md:KeyDescriptor>
        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                                Location="{config['sso_url']}"/>
        <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
                                Location="{config['sso_url']}"/>
    </md:IDPSSODescriptor>
</md:EntityDescriptor>'''

            try:
                response = self.iam.create_saml_provider(
                    SAMLMetadataDocument=metadata_template,
                    Name=config['provider_name']
                )

                return response['SAMLProviderArn']

            except Exception as e:
                print(f"❌ Failed to create SAML provider: {str(e)}")
                return None

        def _create_federation_roles(self, provider_arn, config):
            """Create federated roles"""

            roles = {}

            for role_config in config['roles']:
                role_name = role_config['name']

                # SAML federation trust policy
                trust_policy = {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Federated": provider_arn
                            },
                            "Action": "sts:AssumeRoleWithSAML",
                            "Condition": {
                                "StringEquals": {
                                    "SAML:aud": "https://signin.aws.amazon.com/saml"
                                }
                            }
                        }
                    ]
                }

                # Add additional conditions
                if role_config.get('conditions'):
                    trust_policy['Statement'][0]['Condition'].update(role_config['conditions'])

                try:
                    # Create role
                    role_response = self.iam.create_role(
                        RoleName=role_name,
                        AssumeRolePolicyDocument=json.dumps(trust_policy),
                        Description=f'SAML federated role: {role_name}',
                        MaxSessionDuration=role_config.get('session_duration', 3600)
                    )

                    role_arn = role_response['Role']['Arn']

                    # Attach permission policies
                    for policy in role_config.get('policies', []):
                        if policy['type'] == 'managed':
                            self.iam.attach_role_policy(
                                RoleName=role_name,
                                PolicyArn=policy['arn']
                            )
                        elif policy['type'] == 'inline':
                            self.iam.put_role_policy(
                                RoleName=role_name,
                                PolicyName=policy['name'],
                                PolicyDocument=json.dumps(policy['document'])
                            )

                    roles[role_name] = {
                        'arn': role_arn,
                        'saml_role_value': f"{role_arn},{provider_arn}"
                    }

                    print(f"✅ Federated role created successfully: {role_name}")

                except Exception as e:
                    print(f"❌ Failed to create federated role {role_name}: {str(e)}")

            return roles

        def _setup_attribute_mapping(self, config):
            """Setup attribute mapping"""

            attribute_mapping = {
                "required_attributes": {
                    "https://aws.amazon.com/SAML/Attributes/Role": {
                        "description": "AWS roles that users can assume",
                        "format": "{RoleArn},{ProviderArn}",
                        "example": "arn:aws:iam::123456789012:role/SAMLRole,arn:aws:iam::123456789012:saml-provider/CompanySAML"
                    },
                    "https://aws.amazon.com/SAML/Attributes/RoleSessionName": {
                        "description": "Role session name",
                        "format": "string",
                        "example": "user.email"
                    }
                },
                "optional_attributes": {
                    "https://aws.amazon.com/SAML/Attributes/SessionDuration": {
                        "description": "Session duration (seconds)",
                        "format": "number",
                        "example": "3600"
                    },
                    "custom_attributes": config.get('custom_attributes', {})
                }
            }

            return attribute_mapping

        def _generate_idp_configuration(self, provider_arn, roles):
            """Generate IdP configuration guide"""

            config_guide = {
                "aws_saml_endpoint": "https://signin.aws.amazon.com/saml",
                "provider_arn": provider_arn,
                "attribute_mappings": {
                    "Role": "https://aws.amazon.com/SAML/Attributes/Role",
                    "RoleSessionName": "https://aws.amazon.com/SAML/Attributes/RoleSessionName"
                },
                "role_mappings": {}
            }

            for role_name, role_info in roles.items():
                config_guide["role_mappings"][role_name] = {
                    "role_arn": role_info['arn'],
                    "saml_attribute_value": role_info['saml_role_value']
                }

            return config_guide

        def create_adfs_integration(self):
            """Create ADFS integration example"""

            adfs_config = {
                'provider_name': 'CompanyADFS',
                'entity_id': 'https://adfs.company.com/adfs/services/trust',
                'sso_url': 'https://adfs.company.com/adfs/ls/',
                'signing_certificate': 'MIICertificateDataHere...',
                'roles': [
                    {
                        'name': 'SAML-Developers',
                        'policies': [
                            {
                                'type': 'managed',
                                'arn': 'arn:aws:iam::aws:policy/PowerUserAccess'
                            }
                        ],
                        'conditions': {
                            "StringEquals": {
                                "SAML:Department": "Engineering"
                            }
                        },
                        'session_duration': 14400  # 4 hours
                    },
                    {
                        'name': 'SAML-Administrators',
                        'policies': [
                            {
                                'type': 'managed',
                                'arn': 'arn:aws:iam::aws:policy/AdministratorAccess'
                            }
                        ],
                        'conditions': {
                            "StringEquals": {
                                "SAML:Role": "Administrator"
                            },
                            "Bool": {
                                "SAML:MultiFactorAuthPresent": "true"
                            }
                        },
                        'session_duration': 3600  # 1 hour
                    },
                    {
                        'name': 'SAML-ReadOnly',
                        'policies': [
                            {
                                'type': 'managed',
                                'arn': 'arn:aws:iam::aws:policy/ReadOnlyAccess'
                            }
                        ]
                    }
                ]
            }

            return self.setup_enterprise_saml_federation(adfs_config)

        def test_saml_assertion(self, saml_response):
            """Test SAML assertion"""

            try:
                # Decode SAML response
                decoded_response = base64.b64decode(saml_response)

                # Parse SAML assertion (simplified version)
                root = ET.fromstring(decoded_response)

                # Extract role information
                roles = []
                for attr in root.iter():
                    if 'Role' in attr.tag:
                        roles.append(attr.text)

                # Use STS AssumeRoleWithSAML
                if roles:
                    role_parts = roles[0].split(',')
                    role_arn = role_parts[0]
                    provider_arn = role_parts[1]

                    response = self.sts.assume_role_with_saml(
                        RoleArn=role_arn,
                        PrincipalArn=provider_arn,
                        SAMLAssertion=saml_response
                    )

                    print("✅ SAML assertion validated successfully")
                    return response['Credentials']

            except Exception as e:
                print(f"❌ SAML assertion test failed: {str(e)}")
                return None

    return SAMLFederationManager()

# Demonstrate SAML federation configuration
saml_manager = configure_saml_federation()

# Create ADFS integration
print("🔧 Configuring Enterprise ADFS Integration:")
# adfs_integration = saml_manager.create_adfs_integration()

print("📋 ADFS Integration Configuration Steps:")
print("  1. ✅ Create SAML identity provider")
print("  2. ✅ Configure federated roles (Developer, Administrator, Read-only)")
print("  3. ✅ Setup attribute mapping")
print("  4. ✅ Generate IdP configuration guide")

9.3 OpenID Connect (OIDC) Integration

9.3.1 Web Identity Federation

def configure_oidc_federation():
    """
    Configure OpenID Connect federated identity
    """

    class OIDCFederationManager:
        def __init__(self):
            self.iam = boto3.client('iam')
            self.sts = boto3.client('sts')

        def setup_oidc_provider(self, provider_config):
            """Setup OIDC identity provider"""

            try:
                # Create OIDC provider
                response = self.iam.create_open_id_connect_provider(
                    Url=provider_config['issuer_url'],
                    ClientIDList=provider_config['client_ids'],
                    ThumbprintList=provider_config['thumbprints'],
                    Tags=[
                        {'Key': 'Provider', 'Value': provider_config['name']},
                        {'Key': 'Type', 'Value': 'OIDC'}
                    ]
                )

                provider_arn = response['OpenIDConnectProviderArn']
                print(f"✅ OIDC provider created successfully: {provider_arn}")

                # Create federated roles
                roles = self._create_oidc_roles(provider_arn, provider_config)

                return {
                    'provider_arn': provider_arn,
                    'roles': roles,
                    'client_config': self._generate_client_config(provider_arn, roles)
                }

            except Exception as e:
                print(f"❌ Failed to create OIDC provider: {str(e)}")
                return None

        def _create_oidc_roles(self, provider_arn, config):
            """Create OIDC federated roles"""

            roles = {}

            for role_config in config['roles']:
                role_name = role_config['name']

                # Build trust policy
                conditions = {
                    "StringEquals": {
                        f"{config['issuer_url'].replace('https://', '')}:aud": config['client_ids'][0]
                    }
                }

                # Add additional conditions
                if role_config.get('conditions'):
                    conditions.update(role_config['conditions'])

                trust_policy = {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Federated": provider_arn
                            },
                            "Action": "sts:AssumeRoleWithWebIdentity",
                            "Condition": conditions
                        }
                    ]
                }

                try:
                    # Create role
                    role_response = self.iam.create_role(
                        RoleName=role_name,
                        AssumeRolePolicyDocument=json.dumps(trust_policy),
                        Description=f'OIDC federated role: {role_name}'
                    )

                    role_arn = role_response['Role']['Arn']

                    # Attach permission policies
                    for policy in role_config.get('policies', []):
                        self._attach_policy_to_role(role_name, policy)

                    roles[role_name] = role_arn
                    print(f"✅ OIDC role created successfully: {role_name}")

                except Exception as e:
                    print(f"❌ Failed to create OIDC role {role_name}: {str(e)}")

            return roles

        def _attach_policy_to_role(self, role_name, policy):
            """Attach policy to role"""

            if policy['type'] == 'managed':
                self.iam.attach_role_policy(
                    RoleName=role_name,
                    PolicyArn=policy['arn']
                )
            elif policy['type'] == 'inline':
                self.iam.put_role_policy(
                    RoleName=role_name,
                    PolicyName=policy['name'],
                    PolicyDocument=json.dumps(policy['document'])
                )

        def _generate_client_config(self, provider_arn, roles):
            """Generate client configuration"""

            return {
                'provider_arn': provider_arn,
                'available_roles': roles,
                'assume_role_endpoint': 'https://sts.amazonaws.com/',
                'token_usage': {
                    'id_token': 'Used for authentication',
                    'access_token': 'Used for API access',
                    'refresh_token': 'Used to refresh tokens'
                }
            }

        def setup_google_oidc(self):
            """Setup Google OIDC integration"""

            google_config = {
                'name': 'GoogleOIDC',
                'issuer_url': 'https://accounts.google.com',
                'client_ids': ['your-google-client-id.apps.googleusercontent.com'],
                'thumbprints': ['1f494a0f3e59b0c9e89de97c9eac32d1ba7c6cfb'],  # Google certificate thumbprint
                'roles': [
                    {
                        'name': 'GoogleUser-S3Access',
                        'policies': [
                            {
                                'type': 'inline',
                                'name': 'GoogleUserS3Policy',
                                'document': {
                                    "Version": "2012-10-17",
                                    "Statement": [
                                        {
                                            "Effect": "Allow",
                                            "Action": [
                                                "s3:GetObject",
                                                "s3:PutObject"
                                            ],
                                            "Resource": "arn:aws:s3:::user-data/${accounts.google.com:sub}/*"
                                        }
                                    ]
                                }
                            }
                        ],
                        'conditions': {
                            "StringLike": {
                                "accounts.google.com:email": "*@company.com"
                            }
                        }
                    }
                ]
            }

            return self.setup_oidc_provider(google_config)

        def setup_github_actions_oidc(self):
            """Setup GitHub Actions OIDC integration"""

            github_config = {
                'name': 'GitHubActions',
                'issuer_url': 'https://token.actions.githubusercontent.com',
                'client_ids': ['sts.amazonaws.com'],
                'thumbprints': ['6938fd4d98bab03faadb97b34396831e3780aea1'],
                'roles': [
                    {
                        'name': 'GitHubActions-DeployRole',
                        'policies': [
                            {
                                'type': 'inline',
                                'name': 'GitHubDeployPolicy',
                                'document': {
                                    "Version": "2012-10-17",
                                    "Statement": [
                                        {
                                            "Effect": "Allow",
                                            "Action": [
                                                "s3:PutObject",
                                                "s3:GetObject",
                                                "s3:DeleteObject"
                                            ],
                                            "Resource": "arn:aws:s3:::deployment-bucket/*"
                                        },
                                        {
                                            "Effect": "Allow",
                                            "Action": [
                                                "lambda:UpdateFunctionCode",
                                                "lambda:UpdateFunctionConfiguration"
                                            ],
                                            "Resource": "arn:aws:lambda:*:*:function:prod-*"
                                        }
                                    ]
                                }
                            }
                        ],
                        'conditions': {
                            "StringEquals": {
                                "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                            },
                            "StringLike": {
                                "token.actions.githubusercontent.com:sub": "repo:company/repository:*"
                            }
                        }
                    }
                ]
            }

            return self.setup_oidc_provider(github_config)

        def test_web_identity_token(self, id_token, role_arn, session_name="WebIdentitySession"):
            """Test web identity token"""

            try:
                response = self.sts.assume_role_with_web_identity(
                    RoleArn=role_arn,
                    RoleSessionName=session_name,
                    WebIdentityToken=id_token
                )

                credentials = response['Credentials']

                print("✅ Web identity token validated successfully")
                print(f"   AccessKeyId: {credentials['AccessKeyId'][:10]}...")
                print(f"   Expiration: {credentials['Expiration']}")

                return credentials

            except Exception as e:
                print(f"❌ Web identity token validation failed: {str(e)}")
                return None

        def generate_client_code_examples(self, provider_config):
            """Generate client code examples"""

            examples = {
                'javascript': f'''
// JavaScript example - Getting AWS credentials using OIDC token
const AWS = require('aws-sdk');

async function assumeRoleWithWebIdentity(idToken, roleArn) {{
    const sts = new AWS.STS();

    try {{
        const result = await sts.assumeRoleWithWebIdentity({{
            RoleArn: roleArn,
            RoleSessionName: 'WebSession',
            WebIdentityToken: idToken
        }}).promise();

        // Create AWS service client using temporary credentials
        const s3 = new AWS.S3({{
            accessKeyId: result.Credentials.AccessKeyId,
            secretAccessKey: result.Credentials.SecretAccessKey,
            sessionToken: result.Credentials.SessionToken
        }});

        return s3;

    }} catch (error) {{
        console.error('AssumeRole failed:', error);
        throw error;
    }}
}}
''',
                'python': f'''
# Python example - Getting AWS credentials using OIDC token
import boto3

def assume_role_with_web_identity(id_token, role_arn):
    sts = boto3.client('sts')

    try:
        response = sts.assume_role_with_web_identity(
            RoleArn=role_arn,
            RoleSessionName='WebSession',
            WebIdentityToken=id_token
        )

        credentials = response['Credentials']

        # Create AWS service client using temporary credentials
        s3 = boto3.client(
            's3',
            aws_access_key_id=credentials['AccessKeyId'],
            aws_secret_access_key=credentials['SecretAccessKey'],
            aws_session_token=credentials['SessionToken']
        )

        return s3

    except Exception as e:
        print(f"AssumeRole failed: {{e}}")
        raise
''',
                'github_actions': '''
# GitHub Actions workflow example
name: Deploy to AWS

on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActions-DeployRole
          role-session-name: GitHubActions-Deploy
          aws-region: us-east-1

      - name: Deploy to S3
        run: |
          aws s3 sync ./build s3://deployment-bucket/
'''
            }

            return examples

    return OIDCFederationManager()

# Demonstrate OIDC federation configuration
oidc_manager = configure_oidc_federation()

# Setup different OIDC providers
print("🔧 Configuring OIDC Federated Identity:")

# Google OIDC integration
print("\n📱 Google OIDC Integration:")
# google_integration = oidc_manager.setup_google_oidc()

# GitHub Actions integration
print("\n🔧 GitHub Actions OIDC Integration:")
# github_integration = oidc_manager.setup_github_actions_oidc()

print("\n📋 OIDC Integration Configuration Complete:")
print("  ✅ Google identity provider configured")
print("  ✅ GitHub Actions integration configured")
print("  ✅ Web identity federated roles created")
print("  ✅ Client code examples generated")

# Generate client code examples
code_examples = oidc_manager.generate_client_code_examples({})
print("\n📝 Client integration code generated")

9.4 AWS SSO (Identity Center) Configuration

9.4.1 Enterprise-Level Single Sign-On Solution

def configure_aws_sso():
    """
    Configure AWS SSO (Identity Center)
    """

    class AWSIdentityCenterManager:
        def __init__(self):
            self.sso_admin = boto3.client('sso-admin')
            self.identitystore = boto3.client('identitystore')
            self.organizations = boto3.client('organizations')

        def setup_identity_center(self, config):
            """Setup AWS Identity Center"""

            try:
                # Get SSO instance information
                sso_instances = self.sso_admin.list_instances()

                if not sso_instances['Instances']:
                    print("❌ No AWS SSO instance found, please enable AWS SSO first")
                    return None

                sso_instance = sso_instances['Instances'][0]
                instance_arn = sso_instance['InstanceArn']
                identity_store_id = sso_instance['IdentityStoreId']

                print(f"✅ Found SSO instance: {instance_arn}")

                # Configure permission sets
                permission_sets = self._create_permission_sets(instance_arn, config)

                # Configure account assignments
                account_assignments = self._setup_account_assignments(
                    instance_arn, identity_store_id, permission_sets, config
                )

                # Configure external identity source
                if config.get('external_identity_source'):
                    self._configure_external_identity_source(instance_arn, config)

                return {
                    'instance_arn': instance_arn,
                    'identity_store_id': identity_store_id,
                    'permission_sets': permission_sets,
                    'account_assignments': account_assignments
                }

            except Exception as e:
                print(f"❌ Failed to configure Identity Center: {str(e)}")
                return None

        def _create_permission_sets(self, instance_arn, config):
            """Create permission sets"""

            permission_sets = {}

            for ps_config in config['permission_sets']:
                try:
                    # Create permission set
                    response = self.sso_admin.create_permission_set(
                        Name=ps_config['name'],
                        Description=ps_config.get('description', ''),
                        InstanceArn=instance_arn,
                        SessionDuration=ps_config.get('session_duration', 'PT1H'),  # 1 hour
                        RelayState=ps_config.get('relay_state', '')
                    )

                    ps_arn = response['PermissionSet']['PermissionSetArn']

                    # Attach AWS managed policies
                    for policy_arn in ps_config.get('managed_policies', []):
                        self.sso_admin.attach_managed_policy_to_permission_set(
                            InstanceArn=instance_arn,
                            PermissionSetArn=ps_arn,
                            ManagedPolicyArn=policy_arn
                        )

                    # Add inline policy
                    if ps_config.get('inline_policy'):
                        self.sso_admin.put_inline_policy_to_permission_set(
                            InstanceArn=instance_arn,
                            PermissionSetArn=ps_arn,
                            InlinePolicy=json.dumps(ps_config['inline_policy'])
                        )

                    # Configure permissions boundary
                    if ps_config.get('permissions_boundary'):
                        self.sso_admin.put_permissions_boundary_to_permission_set(
                            InstanceArn=instance_arn,
                            PermissionSetArn=ps_arn,
                            PermissionsBoundary={
                                'ManagedPolicyArn': ps_config['permissions_boundary']
                            }
                        )

                    permission_sets[ps_config['name']] = ps_arn
                    print(f"✅ Permission set created successfully: {ps_config['name']}")

                except Exception as e:
                    print(f"❌ Failed to create permission set {ps_config['name']}: {str(e)}")

            return permission_sets

        def _setup_account_assignments(self, instance_arn, identity_store_id, permission_sets, config):
            """Setup account assignments"""

            assignments = []

            # Get accounts in organization
            accounts = self.organizations.list_accounts()
            account_map = {acc['Name']: acc['Id'] for acc in accounts['Accounts']}

            for assignment in config.get('account_assignments', []):
                try:
                    account_id = account_map.get(assignment['account_name'], assignment.get('account_id'))
                    ps_arn = permission_sets.get(assignment['permission_set'])

                    if not account_id or not ps_arn:
                        print(f"⚠️ Skipping assignment - account or permission set not found: {assignment}")
                        continue

                    # Assign to users
                    if assignment.get('users'):
                        for user_name in assignment['users']:
                            user_id = self._get_user_id(identity_store_id, user_name)
                            if user_id:
                                self.sso_admin.create_account_assignment(
                                    InstanceArn=instance_arn,
                                    TargetId=account_id,
                                    TargetType='AWS_ACCOUNT',
                                    PermissionSetArn=ps_arn,
                                    PrincipalType='USER',
                                    PrincipalId=user_id
                                )
                                assignments.append({
                                    'type': 'user',
                                    'principal': user_name,
                                    'account': account_id,
                                    'permission_set': assignment['permission_set']
                                })

                    # Assign to groups
                    if assignment.get('groups'):
                        for group_name in assignment['groups']:
                            group_id = self._get_group_id(identity_store_id, group_name)
                            if group_id:
                                self.sso_admin.create_account_assignment(
                                    InstanceArn=instance_arn,
                                    TargetId=account_id,
                                    TargetType='AWS_ACCOUNT',
                                    PermissionSetArn=ps_arn,
                                    PrincipalType='GROUP',
                                    PrincipalId=group_id
                                )
                                assignments.append({
                                    'type': 'group',
                                    'principal': group_name,
                                    'account': account_id,
                                    'permission_set': assignment['permission_set']
                                })

                    print(f"✅ Account assignment configured successfully: {assignment['permission_set']} -> {assignment['account_name']}")

                except Exception as e:
                    print(f"❌ Account assignment failed: {str(e)}")

            return assignments

        def _get_user_id(self, identity_store_id, user_name):
            """Get user ID"""
            try:
                response = self.identitystore.list_users(
                    IdentityStoreId=identity_store_id,
                    Filters=[
                        {
                            'AttributePath': 'UserName',
                            'AttributeValue': user_name
                        }
                    ]
                )

                if response['Users']:
                    return response['Users'][0]['UserId']

            except Exception as e:
                print(f"⚠️ Failed to get user ID for {user_name}: {str(e)}")

            return None

        def _get_group_id(self, identity_store_id, group_name):
            """Get group ID"""
            try:
                response = self.identitystore.list_groups(
                    IdentityStoreId=identity_store_id,
                    Filters=[
                        {
                            'AttributePath': 'DisplayName',
                            'AttributeValue': group_name
                        }
                    ]
                )

                if response['Groups']:
                    return response['Groups'][0]['GroupId']

            except Exception as e:
                print(f"⚠️ Failed to get group ID for {group_name}: {str(e)}")

            return None

        def _configure_external_identity_source(self, instance_arn, config):
            """Configure external identity source"""

            external_config = config['external_identity_source']

            if external_config['type'] == 'ActiveDirectory':
                # Configure AD connector
                print("🔗 Configuring Active Directory connector...")
                # Actual implementation requires calling appropriate APIs

            elif external_config['type'] == 'SAML':
                # Configure SAML identity source
                print("🔗 Configuring SAML identity source...")
                # Actual implementation requires calling appropriate APIs

        def create_enterprise_sso_config(self):
            """Create enterprise SSO configuration example"""

            config = {
                'permission_sets': [
                    {
                        'name': 'AdministratorAccess',
                        'description': 'Full administrator access',
                        'session_duration': 'PT4H',  # 4 hours
                        'managed_policies': [
                            'arn:aws:iam::aws:policy/AdministratorAccess'
                        ],
                        'permissions_boundary': 'arn:aws:iam::123456789012:policy/AdminPermissionsBoundary'
                    },
                    {
                        'name': 'DeveloperAccess',
                        'description': 'Developer access permissions',
                        'session_duration': 'PT8H',  # 8 hours
                        'managed_policies': [
                            'arn:aws:iam::aws:policy/PowerUserAccess'
                        ],
                        'inline_policy': {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Deny",
                                    "Action": [
                                        "iam:CreateRole",
                                        "iam:DeleteRole",
                                        "organizations:*"
                                    ],
                                    "Resource": "*"
                                }
                            ]
                        }
                    },
                    {
                        'name': 'ReadOnlyAccess',
                        'description': 'Read-only access permissions',
                        'session_duration': 'PT12H',  # 12 hours
                        'managed_policies': [
                            'arn:aws:iam::aws:policy/ReadOnlyAccess'
                        ]
                    },
                    {
                        'name': 'DatabaseAdmin',
                        'description': 'Database administrator permissions',
                        'session_duration': 'PT4H',
                        'inline_policy': {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "rds:*",
                                        "dynamodb:*",
                                        "elasticache:*"
                                    ],
                                    "Resource": "*"
                                },
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "s3:GetObject",
                                        "s3:PutObject"
                                    ],
                                    "Resource": "arn:aws:s3:::database-backups/*"
                                }
                            ]
                        }
                    }
                ],
                'account_assignments': [
                    {
                        'account_name': 'Production',
                        'permission_set': 'AdministratorAccess',
                        'groups': ['Administrators']
                    },
                    {
                        'account_name': 'Production',
                        'permission_set': 'DatabaseAdmin',
                        'groups': ['DatabaseAdmins']
                    },
                    {
                        'account_name': 'Development',
                        'permission_set': 'DeveloperAccess',
                        'groups': ['Developers']
                    },
                    {
                        'account_name': 'Development',
                        'permission_set': 'ReadOnlyAccess',
                        'users': ['john.doe', 'jane.smith']
                    }
                ],
                'external_identity_source': {
                    'type': 'ActiveDirectory',
                    'domain': 'company.com',
                    'directory_id': 'd-1234567890'
                }
            }

            return self.setup_identity_center(config)

    return AWSIdentityCenterManager()

# Demonstrate AWS SSO configuration
sso_manager = configure_aws_sso()

print("🔧 Configuring AWS Identity Center:")
# enterprise_sso = sso_manager.create_enterprise_sso_config()

print("\n📋 Identity Center Configuration Complete:")
print("  ✅ Permission sets created (Administrator, Developer, Read-only, Database Administrator)")
print("  ✅ Account assignments configured")
print("  ✅ External identity source configured")
print("  ✅ Session management configured")

print("\n🔗 User Access Flow:")
print("  1. User accesses SSO portal")
print("  2. Authenticates through enterprise identity provider")
print("  3. Selects AWS account and role to access")
print("  4. Obtains temporary credentials to access AWS resources")

Summary

This chapter introduced complete solutions for AWS federated identity and single sign-on:

  1. Federated Identity Basics: Understanding federated identity concepts, architecture, and trust models
  2. SAML 2.0 Integration: Enterprise-level SAML federation configuration and ADFS integration
  3. OIDC Integration: Web identity federation and GitHub Actions integration
  4. AWS SSO: Identity Center configuration and enterprise-level single sign-on

Through federated identity, you can achieve:

  • Unified enterprise identity management
  • Secure single sign-on experience
  • Flexible role mapping and permission management
  • Seamless integration with existing identity infrastructure

In the next chapter, we will learn about IAM access analysis and auditing mechanisms.