📑 Table of Contents

SAML 2.0 (Security Assertion Markup Language) remains the dominant protocol for enterprise Single Sign-On, used by thousands of organizations to connect their workforce to hundreds of applications. While OpenID Connect is preferred for modern web applications, SAML is essential knowledge for any IAM professional because it powers the majority of enterprise software integrations. In this lab, you'll build a complete SAML SSO implementation from scratch, understanding both the Identity Provider (IdP) and Service Provider (SP) sides of the equation.

🎯 Lab Overview & SAML Fundamentals

SAML 2.0 is an XML-based open standard for exchanging authentication and authorization data between parties—specifically, between an Identity Provider (IdP) and a Service Provider (SP). When you click "Login with SSO" or "Login with Corporate Account" on enterprise software, you're almost always using SAML. Understanding SAML is essential because it connects enterprise identity systems (like Active Directory, Okta, or Azure AD) to thousands of SaaS applications (Salesforce, Workday, ServiceNow, etc.). In this lab, you'll configure both sides of a SAML integration, gaining the skills to troubleshoot SSO issues that plague enterprise IT teams daily.

✅ Prerequisites

  • Completed LAB 5: Keycloak basics, realm and user creation
  • Docker & Docker Compose: Container management skills
  • Basic understanding of: HTTP, XML, certificates, and web security

What You Will Build

Learning Objectives

🏢 Enterprise Scenario: SaaS Integration Project

You're the IAM Engineer at GlobalCorp, and the IT Director has a challenge:

"We just acquired three new SaaS applications—Salesforce for sales, Workday for HR, and ServiceNow for IT service management. Each has its own login, and employees are drowning in passwords. We need Single Sign-On so employees can access all three with their corporate credentials. The vendors say they support SAML. Can you make this work?"

  • Connect corporate identity to SaaS applications without sharing passwords
  • Provision user attributes so applications know employee roles and departments
  • Maintain security with signed and encrypted SAML messages
  • Enable instant deprovisioning when employees leave

📚 SAML 2.0 Deep Dive: Protocol & Components

Before implementing SAML, you must understand its components and how they work together. SAML is more complex than OIDC because it predates modern web architecture and uses XML rather than JSON. However, this complexity provides features that OIDC lacks, particularly around enterprise federation scenarios.

📖 What is SAML?

SAML (Security Assertion Markup Language) is an XML-based framework for communicating authentication, authorization, and attribute information. Key characteristics:

  • XML-based: All messages are XML documents with defined schemas
  • Browser-based: Uses HTTP redirects and POST to move assertions between parties
  • Federation-focused: Designed for cross-domain SSO between organizations
  • Mature standard: SAML 2.0 released in 2005, widely adopted since 2010

SAML Roles

🏛️ Identity Provider (IdP)

The system that authenticates users and issues SAML assertions. Examples: Okta, Azure AD, Keycloak, Ping Identity. The IdP is the "source of truth" for user identity.

📱 Service Provider (SP)

The application that users want to access. The SP trusts the IdP to authenticate users. Examples: Salesforce, Workday, AWS Console, any SAML-enabled app.

👤 Principal (User)

The human or system seeking access to the Service Provider. The Principal authenticates at the IdP and receives an assertion to present to the SP.

📜 SAML Assertion

The XML document containing authentication and attribute statements. Signed by the IdP, verified by the SP. The "proof" that the user authenticated successfully.

SAML Components

ComponentDescriptionPurpose
AssertionXML document with authentication/attribute statementsCarries identity information from IdP to SP
ProtocolRequest/response message formatsDefines how to ask for and receive assertions
BindingsHow SAML messages travel (HTTP POST, Redirect)Transport mechanisms for SAML messages
ProfilesCombinations of assertions, protocols, and bindingsComplete use cases like Web Browser SSO
MetadataXML describing IdP/SP capabilities and certificatesEstablishes trust between parties

▼ SAML WEB BROWSER SSO FLOW (SP-INITIATED) ▼

1
User Accesses Service Provider

User clicks login or accesses protected resource at SP (e.g., salesforce.com)

2
SP Generates SAML AuthnRequest

SP creates XML request asking IdP to authenticate user, redirects browser to IdP

3
User Authenticates at IdP

User enters credentials at IdP login page (username/password, MFA, etc.)

4
IdP Generates SAML Response

IdP creates signed SAML assertion with user identity and attributes

5
Browser POSTs Response to SP

IdP returns HTML form that auto-submits SAML response to SP's ACS URL

6
SP Validates and Creates Session

SP verifies signature, checks conditions, extracts attributes, logs user in

🔍 Deep Dive: SAML Assertion Structure

A SAML assertion contains three types of statements:

  • Authentication Statement: Confirms the user authenticated at a specific time using a specific method
  • Attribute Statement: Contains user attributes (email, name, groups, roles)
  • Authorization Decision Statement: (Rarely used) States whether user is authorized for a resource

Critical assertion elements include:

  • Issuer: The IdP that created the assertion (must match SP's trusted IdP)
  • Subject: The user's identifier (NameID)
  • Conditions: Validity constraints (NotBefore, NotOnOrAfter, AudienceRestriction)
  • Signature: XML digital signature proving assertion authenticity

SAML vs OIDC Comparison

AspectSAML 2.0OpenID Connect
Data FormatXMLJSON (JWT)
TransportHTTP Redirect, POSTHTTP REST APIs
Token SizeLarge (XML verbose)Compact (Base64 JWT)
Best ForEnterprise SSO, legacy appsModern web/mobile apps
Mobile SupportPoor (XML parsing heavy)Excellent (JSON native)
AdoptionEnterprise SaaS (Salesforce, Workday)Consumer apps (Google, Facebook)

📋 Prerequisites & Lab Environment

This lab builds on LAB 5's Keycloak deployment. We'll add a Python Flask application as a SAML Service Provider, demonstrating the complete SSO flow.

Required Components

ComponentRequirementPurpose
LAB 5 EnvironmentKeycloak running on port 8080SAML Identity Provider
DockerDocker 20.10+Container runtime
PythonPython 3.9+ (in container)SAML SP application

Device Badges Legend

Local MachineDocker host / terminal
Keycloak ConsoleIdP admin interface
SP ApplicationService Provider
Web BrowserTesting SSO flow

🔐 Module 1: Configure Keycloak as SAML Identity Provider

Module 1: SAML IdP Configuration

Configure Keycloak to issue SAML assertions for Service Provider applications.

⏱️ 30-45 minutes🎯 5 steps📍 Keycloak Console

Keycloak can act as both an OIDC and SAML Identity Provider. In this module, we'll create a SAML client configuration that defines how Keycloak will issue SAML assertions to our Service Provider application.

1

Verify Keycloak Environment

Ensure your LAB 5 Keycloak environment is running with the techstart realm configured.

Local Machine
# Verify Keycloak is running from LAB 5 cd ~/lab5-iam docker compose ps # If not running, start it docker compose up -d # Wait for Keycloak to be ready sleep 30 curl -s http://localhost:8080/health/ready | grep -q "UP" && echo "Keycloak is ready"
2

Create SAML Client in Keycloak

Create a new client configured for SAML protocol. This represents our Service Provider in Keycloak.

Keycloak Console
1. Login to Keycloak: http://localhost:8080 2. Select "techstart" realm (top-left dropdown) 3. Navigate to: Clients → Create client General Settings: - Client type: SAML - Client ID: saml-demo-app - Click "Next" SAML Capabilities: - Name ID format: email - Force POST binding: ON - Include AuthnStatement: ON - Sign assertions: ON - Click "Next" Login Settings: - Root URL: http://localhost:5000 - Valid redirect URIs: http://localhost:5000/* - Master SAML Processing URL: http://localhost:5000/saml/acs - Click "Save"
3

Configure SAML Client Settings

Fine-tune the SAML client settings for proper SSO behavior.

Keycloak Console
Navigate to: Clients → saml-demo-app → Settings Verify/Update these settings: - Client Signature Required: OFF (for lab simplicity) - Force POST Binding: ON - Front Channel Logout: ON - Force Name ID Format: ON - Name ID Format: email Navigate to: Keys tab - Client signature required: OFF - Note: In production, enable client signing Click "Save"
4

Download IdP SAML Metadata

Export Keycloak's SAML metadata, which the Service Provider needs to trust the IdP.

Keycloak Console
The IdP metadata URL is: http://localhost:8080/realms/techstart/protocol/saml/descriptor Navigate to: Realm Settings → General → Endpoints Click: "SAML 2.0 Identity Provider Metadata" This XML file contains: - IdP Entity ID - SSO endpoint URLs - Signing certificates - Supported bindings
5

Verify IdP Metadata

Download and examine the IdP metadata to understand its structure.

Local Machine
# Download IdP metadata curl -s http://localhost:8080/realms/techstart/protocol/saml/descriptor > ~/lab5-iam/idp-metadata.xml # View key elements echo "=== IdP Entity ID ===" grep -o 'entityID="[^"]*"' ~/lab5-iam/idp-metadata.xml | head -1 echo "=== SSO Service URL ===" grep -o 'Location="[^"]*sso[^"]*"' ~/lab5-iam/idp-metadata.xml | head -1 echo "=== Signing Certificate ===" grep -A1 'use="signing"' ~/lab5-iam/idp-metadata.xml | tail -1 | head -c 100 echo "..."

✅ Module 1 Complete!

Keycloak is now configured as a SAML Identity Provider. In Module 2, we'll create the Service Provider application.

📱 Module 2: Deploy SAML Service Provider Application

Module 2: Build a SAML-Protected Application

Create a Python Flask application that authenticates users via SAML SSO.

⏱️ 45-60 minutes🎯 5 steps📍 Local Machine

A SAML Service Provider is any application that delegates authentication to an Identity Provider. We'll build a Flask application using the python3-saml library, which handles SAML message parsing, signature validation, and session management.

6

Create SP Application Directory

Local Machine
# Create directory structure for SAML SP mkdir -p ~/lab5-iam/saml-sp/{saml,templates} cd ~/lab5-iam/saml-sp echo "SAML SP directory created"
7

Create Flask SAML Application

Create the main Flask application with SAML authentication endpoints.

Local Machine
cat > ~/lab5-iam/saml-sp/app.py << 'EOF' """ SAML Service Provider Demo Application Identity Bytes - LAB 6: SAML 2.0 Enterprise SSO This Flask application demonstrates SAML SP functionality: - Initiates SAML authentication requests - Processes SAML responses from IdP - Extracts user attributes from assertions """ from flask import Flask, request, redirect, session, render_template_string from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.utils import OneLogin_Saml2_Utils import os app = Flask(__name__) app.secret_key = os.urandom(24) # Session encryption key # HTML Templates LOGIN_TEMPLATE = """ SAML SP - Login

🔐 SAML Service Provider

This application requires SAML authentication.

Login with SSO (SAML)
""" DASHBOARD_TEMPLATE = """ SAML SP - Dashboard

✅ SAML Authentication Successful!

👤 User Information

NameID: {{ nameid }}

Session Index: {{ session_index }}

📋 SAML Attributes

{{ attributes }}
Logout (SAML SLO)
""" def init_saml_auth(req): """Initialize SAML auth object with request data""" auth = OneLogin_Saml2_Auth(req, custom_base_path=os.path.join(os.path.dirname(__file__), 'saml')) return auth def prepare_flask_request(request): """Convert Flask request to format expected by python3-saml""" url_data = request.url.split('?') return { 'https': 'on' if request.scheme == 'https' else 'off', 'http_host': request.host, 'server_port': request.environ.get('SERVER_PORT', '5000'), 'script_name': request.path, 'get_data': request.args.copy(), 'post_data': request.form.copy(), 'query_string': request.query_string.decode('utf-8') } @app.route('/') def index(): """Home page - show login or dashboard based on session""" if 'saml_user' in session: return render_template_string(DASHBOARD_TEMPLATE, nameid=session.get('saml_nameid', 'Unknown'), session_index=session.get('saml_session_index', 'Unknown'), attributes=str(session.get('saml_attributes', {}))) return LOGIN_TEMPLATE @app.route('/saml/login') def saml_login(): """Initiate SAML authentication - redirect to IdP""" req = prepare_flask_request(request) auth = init_saml_auth(req) # Generate AuthnRequest and redirect to IdP return redirect(auth.login()) @app.route('/saml/acs', methods=['POST']) def saml_acs(): """Assertion Consumer Service - process SAML response from IdP""" req = prepare_flask_request(request) auth = init_saml_auth(req) # Process the SAML response auth.process_response() errors = auth.get_errors() if errors: return f"SAML Error: {', '.join(errors)}
Reason: {auth.get_last_error_reason()}", 400 if not auth.is_authenticated(): return "Authentication failed", 401 # Store user info in session session['saml_user'] = True session['saml_nameid'] = auth.get_nameid() session['saml_session_index'] = auth.get_session_index() session['saml_attributes'] = auth.get_attributes() # Redirect to relay state or home relay_state = request.form.get('RelayState', '/') return redirect(relay_state) @app.route('/saml/logout') def saml_logout(): """Initiate SAML Single Logout""" req = prepare_flask_request(request) auth = init_saml_auth(req) name_id = session.get('saml_nameid') session_index = session.get('saml_session_index') # Clear local session session.clear() # Redirect to IdP for SLO return redirect(auth.logout(name_id=name_id, session_index=session_index)) @app.route('/saml/sls') def saml_sls(): """Single Logout Service - process logout response from IdP""" req = prepare_flask_request(request) auth = init_saml_auth(req) # Process logout response auth.process_slo() errors = auth.get_errors() if errors: return f"SLO Error: {', '.join(errors)}", 400 session.clear() return redirect('/') @app.route('/saml/metadata') def saml_metadata(): """Expose SP metadata for IdP configuration""" req = prepare_flask_request(request) auth = init_saml_auth(req) settings = auth.get_settings() metadata = settings.get_sp_metadata() errors = settings.validate_metadata(metadata) if errors: return f"Metadata Error: {', '.join(errors)}", 500 return metadata, 200, {'Content-Type': 'application/xml'} if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) EOF echo "Flask SAML application created"
8

Create SAML Configuration

Create the python3-saml settings files that define SP and IdP configuration.

Local Machine
cat > ~/lab5-iam/saml-sp/saml/settings.json << 'EOF' { "strict": false, "debug": true, "sp": { "entityId": "saml-demo-app", "assertionConsumerService": { "url": "http://localhost:5000/saml/acs", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, "singleLogoutService": { "url": "http://localhost:5000/saml/sls", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" }, "idp": { "entityId": "http://localhost:8080/realms/techstart", "singleSignOnService": { "url": "http://localhost:8080/realms/techstart/protocol/saml", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "singleLogoutService": { "url": "http://localhost:8080/realms/techstart/protocol/saml", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "x509cert": "PASTE_IDP_CERTIFICATE_HERE" } } EOF cat > ~/lab5-iam/saml-sp/saml/advanced_settings.json << 'EOF' { "security": { "nameIdEncrypted": false, "authnRequestsSigned": false, "logoutRequestSigned": false, "logoutResponseSigned": false, "signMetadata": false, "wantMessagesSigned": false, "wantAssertionsSigned": true, "wantNameIdEncrypted": false, "requestedAuthnContext": false, "wantAssertionsEncrypted": false, "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256" }, "contactPerson": { "technical": { "givenName": "IAM Admin", "emailAddress": "iam@techstart.local" } }, "organization": { "en-US": { "name": "TechStart Inc", "displayname": "TechStart", "url": "http://localhost:5000" } } } EOF echo "SAML configuration files created"
9

Extract and Configure IdP Certificate

Extract the signing certificate from Keycloak's metadata and add it to the SP configuration.

Local Machine
# Extract the signing certificate from IdP metadata # This certificate is used by SP to verify SAML assertion signatures cd ~/lab5-iam # Download fresh metadata curl -s http://localhost:8080/realms/techstart/protocol/saml/descriptor > idp-metadata.xml # Extract certificate (remove whitespace and newlines) CERT=$(grep -A100 'use="signing"' idp-metadata.xml | grep -oP '(?<=).*?(?=)' | tr -d '\n ' | head -1) # Update settings.json with the certificate cd saml-sp/saml sed -i "s|PASTE_IDP_CERTIFICATE_HERE|$CERT|" settings.json echo "Certificate extracted and configured" echo "Certificate preview (first 50 chars): ${CERT:0:50}..."
10

Create Docker Configuration for SP

Create Docker configuration to run the Flask SAML SP application.

Local Machine
cat > ~/lab5-iam/saml-sp/Dockerfile << 'EOF' FROM python:3.11-slim # Install dependencies for python3-saml (xmlsec) RUN apt-get update && apt-get install -y \ pkg-config \ libxml2-dev \ libxmlsec1-dev \ libxmlsec1-openssl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Install Python packages RUN pip install flask python3-saml # Copy application COPY . . EXPOSE 5000 CMD ["python", "app.py"] EOF # Add to docker-compose.yml cat >> ~/lab5-iam/docker-compose.yml << 'EOF' saml-sp: build: ./saml-sp container_name: saml-sp ports: - "5000:5000" networks: - iam-network depends_on: - keycloak EOF echo "Docker configuration created"

✅ Module 2 Complete!

The SAML Service Provider application is configured. In Module 3, we'll establish trust and test the SSO flow.

🔗 Module 3: SAML Metadata Exchange & Trust

Module 3: Establish Trust Between IdP and SP

Build and test the complete SAML SSO flow.

⏱️ 30-45 minutes🎯 4 steps📍 All Components

Trust in SAML is established through metadata exchange. Each party provides an XML document describing its endpoints, entity ID, and certificates. This module focuses on building and deploying the complete solution.

11

Build and Start SP Container

Local Machine
cd ~/lab5-iam # Build the SAML SP container docker compose build saml-sp # Start the SP docker compose up -d saml-sp # Wait for it to start sleep 10 # Verify SP is running curl -s http://localhost:5000/ | grep -q "SAML Service Provider" && echo "SP is running!"
12

Verify SP Metadata

Retrieve and examine the SP metadata, which can be provided to IdP administrators.

Local Machine
# Get SP metadata curl -s http://localhost:5000/saml/metadata # Save it for reference curl -s http://localhost:5000/saml/metadata > ~/lab5-iam/sp-metadata.xml echo "SP metadata saved to sp-metadata.xml"
13

Test SAML SSO Flow

Test the complete SAML Single Sign-On flow from SP to IdP and back.

Web Browser
1. Open: http://localhost:5000 2. Click "Login with SSO (SAML)" 3. You'll be redirected to Keycloak login 4. Enter credentials: alice.developer / Password123! 5. After authentication, you'll return to the SP 6. Verify: Dashboard shows user info and SAML attributes What's Happening: - SP creates AuthnRequest, redirects to IdP - User authenticates at IdP - IdP creates signed SAML Response with assertion - Browser POSTs response to SP's ACS URL - SP validates signature, extracts attributes, creates session
14

Test Single Logout (SLO)

Test SAML Single Logout to end sessions at both SP and IdP.

Web Browser
1. From the SP dashboard, click "Logout (SAML SLO)" 2. You'll be redirected to IdP for logout 3. IdP terminates your session 4. You're redirected back to SP login page 5. Verify: Try accessing SP again - you must re-authenticate

✅ Module 3 Complete!

The SAML SSO flow is working! In Module 4, we'll configure attribute mapping.

📋 Module 4: Attribute Mapping & Claims

Module 4: Configure User Attributes in SAML

Map IdP user attributes to SAML assertion claims for SP consumption.

⏱️ 30-45 minutes🎯 4 steps📍 Keycloak Console

SAML assertions can carry user attributes beyond just the NameID. These attributes allow the SP to know user details like email, name, groups, and roles without querying a separate directory. Proper attribute mapping is essential for SP applications to function correctly.

15

Configure Client Attribute Mappers

Keycloak Console
Navigate to: Clients → saml-demo-app → Client scopes Click: saml-demo-app-dedicated → Add mapper → By configuration Add these mappers: 1. User Property Mapper: - Name: email - Property: email - SAML Attribute Name: email - SAML Attribute NameFormat: Basic 2. User Property Mapper: - Name: firstName - Property: firstName - SAML Attribute Name: firstName - SAML Attribute NameFormat: Basic 3. User Property Mapper: - Name: lastName - Property: lastName - SAML Attribute Name: lastName - SAML Attribute NameFormat: Basic 4. Role List: - Name: roles - Role attribute name: roles - SAML Attribute NameFormat: Basic - Single Role Attribute: ON
16

Add Group Membership Mapper

Keycloak Console
Add another mapper: Group list: - Name: groups - Group attribute name: groups - SAML Attribute NameFormat: Basic - Single Group Attribute: ON - Full group path: OFF Click "Save"
17

Test Attribute Mapping

Login again and verify attributes appear in the SAML assertion.

Web Browser
1. Open: http://localhost:5000 2. Click "Login with SSO (SAML)" 3. Authenticate as alice.developer 4. On the dashboard, check "SAML Attributes" section You should see attributes like: { 'email': ['alice@techstart.local'], 'firstName': ['Alice'], 'lastName': ['Developer'], 'roles': ['employee', 'developer'], 'groups': ['Engineering', 'Backend'] }

✅ Module 4 Complete!

User attributes are now flowing from IdP to SP in SAML assertions.

🔒 Module 5: SAML Signing & Encryption

Module 5: Production Security Configuration

Implement cryptographic protections for SAML messages.

⏱️ 45-60 minutes🎯 4 steps📍 All Components

In production SAML deployments, messages must be signed (for authenticity) and optionally encrypted (for confidentiality). This module covers the security configurations that make SAML production-ready.

📖 SAML Security Mechanisms

  • Assertion Signing: IdP signs the assertion with its private key; SP verifies with IdP's public certificate
  • Response Signing: The entire SAML response is signed (includes assertion)
  • Request Signing: SP signs AuthnRequest; IdP verifies SP identity
  • Assertion Encryption: IdP encrypts assertion with SP's public key; only SP can decrypt
18

Generate SP Certificate

Create a certificate for the Service Provider to sign requests and decrypt assertions.

Local Machine
# Generate SP private key and self-signed certificate cd ~/lab5-iam/saml-sp/saml openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout sp.key \ -out sp.crt \ -subj "/CN=saml-demo-app/O=TechStart/C=US" echo "SP certificate generated" echo "Certificate details:" openssl x509 -in sp.crt -noout -subject -dates
19

Update SP Configuration for Signing

Add the SP certificate to the SAML configuration.

Local Machine
# Read certificate and key, format for JSON cd ~/lab5-iam/saml-sp/saml SP_CERT=$(cat sp.crt | grep -v "BEGIN\|END" | tr -d '\n') SP_KEY=$(cat sp.key | grep -v "BEGIN\|END" | tr -d '\n') # Update settings.json to include SP certificates # Note: In production, use proper JSON manipulation cat > settings.json << EOF { "strict": true, "debug": true, "sp": { "entityId": "saml-demo-app", "assertionConsumerService": { "url": "http://localhost:5000/saml/acs", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, "singleLogoutService": { "url": "http://localhost:5000/saml/sls", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "x509cert": "$SP_CERT", "privateKey": "$SP_KEY" }, "idp": { "entityId": "http://localhost:8080/realms/techstart", "singleSignOnService": { "url": "http://localhost:8080/realms/techstart/protocol/saml", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "singleLogoutService": { "url": "http://localhost:8080/realms/techstart/protocol/saml", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "x509cert": "$(cat ~/lab5-iam/idp-metadata.xml | grep -A100 'use="signing"' | grep -oP '(?<=).*?(?=)' | tr -d '\n ' | head -1)" } } EOF echo "SP configuration updated with certificates"
20

Rebuild and Test

Local Machine
cd ~/lab5-iam # Rebuild SP with new configuration docker compose build saml-sp docker compose up -d saml-sp sleep 10 echo "SP restarted with signing enabled" # Test SSO again echo "Test at: http://localhost:5000"

✅ Module 5 Complete!

The SP now has certificates for signing and encryption capabilities.

🌐 Module 6: Multi-SP Federation & IdP Discovery

Module 6: Scale to Multiple Service Providers

Configure multiple applications to use the same Identity Provider.

⏱️ 30-45 minutes🎯 3 steps📍 Keycloak Console

Enterprise environments typically have dozens or hundreds of applications using a single IdP. This module demonstrates how to add additional Service Providers and discusses IdP discovery for users who belong to multiple organizations.

21

Create Second SAML Client

Keycloak Console
Navigate to: Clients → Create client Create a second SAML client representing another application: General Settings: - Client type: SAML - Client ID: hr-portal - Click "Next" SAML Settings: - Name ID format: email - Force POST binding: ON - Sign assertions: ON - Click "Next" Login Settings: - Root URL: http://localhost:5001 - Master SAML Processing URL: http://localhost:5001/saml/acs - Click "Save" This demonstrates how a single IdP serves multiple SPs.
22

Understanding IdP-Initiated SSO

Test IdP-initiated SSO where users start at the IdP portal.

Web Browser
IdP-Initiated SSO Flow: 1. User logs into IdP portal directly 2. User clicks on application tile/link 3. IdP generates SAML response without AuthnRequest 4. User arrives at SP already authenticated Test IdP-Initiated flow: 1. Login to Keycloak Account Console: http://localhost:8080/realms/techstart/account 2. After login, access SP directly: http://localhost:5000 Since you're already authenticated at IdP, the SP should recognize your session (if cookies are shared) or prompt for SSO which completes instantly.

✅ Module 6 Complete!

You've learned how to configure multiple Service Providers with a single Identity Provider.

🛡️ SAML Security Vulnerabilities & Best Practices

SAML implementations have been the target of numerous attacks. Understanding these vulnerabilities is essential for building secure SSO systems.

CRITICAL

XML Signature Wrapping (XSW)

Attacker modifies SAML response to inject malicious assertions while keeping the valid signature.

⚔️ Attack

Attacker intercepts SAML response, moves signed assertion, and adds unsigned assertion with elevated privileges. Vulnerable parsers validate the wrong element.

🛡️ Mitigation
  • Use SAML libraries with XSW protection
  • Validate signature covers expected elements
  • Use strict XML parsing
CRITICAL

SAML Response Replay

Attacker captures and replays a valid SAML response to gain unauthorized access.

🛡️ Mitigation
  • Validate NotOnOrAfter conditions strictly
  • Implement assertion ID replay detection
  • Use short assertion validity windows
HIGH

Missing Audience Restriction Validation

SP accepts assertions intended for other Service Providers.

🛡️ Mitigation
  • Always validate AudienceRestriction
  • Ensure audience matches SP Entity ID
  • Reject assertions without audience
HIGH

Comment Injection in NameID

Attacker injects XML comments to bypass NameID validation.

🛡️ Mitigation
  • Canonicalize NameID before comparison
  • Strip comments from XML elements
  • Use exclusive canonicalization

✅ SAML Security Best Practices

  • ✅ Always validate SAML signatures before processing assertions
  • ✅ Check NotBefore and NotOnOrAfter conditions
  • ✅ Validate AudienceRestriction matches your SP Entity ID
  • ✅ Use HTTPS for all SAML endpoints
  • ✅ Implement assertion replay detection
  • ✅ Sign AuthnRequests in production
  • ✅ Consider assertion encryption for sensitive attributes
  • ✅ Use well-maintained SAML libraries (not custom parsing)
  • ✅ Rotate signing certificates before expiration
  • ✅ Log all SAML authentication events

🔧 Troubleshooting SAML Issues

SAML troubleshooting often requires decoding Base64-encoded messages and examining XML structures. Here are common issues and diagnostic approaches.

Issue: "Invalid Signature" Error

Cause: SP cannot verify IdP signature

  • Verify IdP certificate in SP config matches actual IdP cert
  • Check certificate hasn't expired
  • Ensure certificate was extracted correctly (no extra whitespace)
  • Verify IdP is signing assertions (check Keycloak client settings)

Issue: "Destination Mismatch" Error

Cause: ACS URL in SP doesn't match what IdP expects

  • Check Destination attribute in SAML Response
  • Verify SP's ACS URL matches IdP client configuration
  • Check for http vs https mismatches

Issue: User Attributes Not Appearing

Cause: Attribute mappers not configured

  • Check Keycloak client scopes and mappers
  • Verify mapper type (User Property vs User Attribute)
  • Decode SAML response and check AttributeStatement

💡 SAML Debugging Tools

  • SAML Tracer: Browser extension to capture SAML messages
  • base64decode.org: Decode Base64 SAML messages
  • samltool.com: Online SAML debugging utilities

🎓 Key Takeaways

You've built a complete SAML SSO implementation, understanding both the IdP and SP sides of the federation.

Skills Mastered

What's Next

📚 Additional Resources

📖 SAML Specifications
🔧 SAML Tools
🔐 Security Research