IDENTITY BYTES
Senior IAM Architect Track / Phase 1: Core IAM Platform

LAB 03: SAML 2.0 Federation
and Identity Brokering

Federate two independent identity domains with SAML 2.0: Keycloak to Keycloak, IdP to Service Provider, with signed XML assertions you will capture off the wire and dissect element by element. This is the protocol that still runs enterprise SSO, and after this lab you will read it fluently.

IB-SIA-03 Intermediate Est. 3 to 3.5 hours SAML 2.0 / Federation / Brokering / XML Signatures
Section 1

Lab Metadata

AttributeValue
Lab IDIB-SIA-03
TrackIdentity Bytes Senior IAM Architect Track (36 lab curriculum)
DifficultyIntermediate (requires IB-SIA-01 and IB-SIA-02; no prior SAML knowledge assumed)
Core technologiesKeycloak 26.0 (from IB-SIA-02), OpenLDAP (from IB-SIA-01), SAML 2.0 identity brokering, xmllint (libxml2), browser developer tools
Protocols and standardsSAML 2.0 Core, Bindings (HTTP-Redirect, HTTP-POST), Metadata; XML Signature (XML-DSig); RFC 7522 context
Builds onIB-SIA-02 (the northgate realm becomes a SAML Identity Provider; its LDAP federation keeps authenticating users)
Feeds intoIB-SIA-04 (OIDC flows end to end, the modern counterpart), IB-SIA-05 (MFA), IB-SIA-26 (AD DS brokering), IB-SIA-30 (AWS federation)
Section 2

Lab Title and Description

Cross Domain Trust: Federating Two Identity Domains with SAML 2.0 and Dissecting the Assertion

Northgate Financial has acquired Harborview Wealth, a smaller advisory firm with its own identity infrastructure. The integration board's day one requirement is blunt: Harborview's applications must accept Northgate identities from the morning the deal closes, with no account migration, no password resets, and no shared directories. Harborview's platform speaks SAML 2.0, as an enormous share of enterprise software still does: Salesforce, Workday, ServiceNow, the AWS console, and most B2B SaaS all federate over SAML today, which is why the role this track targets lists SAML 2.0 alongside OIDC as required expertise.

In this lab you build that trust relationship end to end. A second Keycloak realm, harborview, plays the acquired company's identity platform and acts as the SAML Service Provider through Keycloak's identity brokering. Your existing northgate realm becomes a SAML Identity Provider, still authenticating users against the Lab 01 LDAP directory. You will exchange metadata between the two domains by hand, extract and pin the IdP's signing certificate, register the Service Provider, map attributes across the trust boundary, and then log in across domains as Amina Smith with a password that exists only in OpenLDAP.

Then comes the part most engineers never do: you will capture the live SAML Response in your browser's developer tools, decode it in the terminal, and read the assertion line by line, Issuer, Subject, Conditions, AudienceRestriction, AttributeStatement, Signature, mapping each XML element to the JWT claim you learned in Lab 02. Two protocols, one mental model. Estimated completion time is 3 to 3.5 hours.

Section 3

Prerequisites

3.1 Prior labs required

LabWhy it is required
IB-SIA-01OpenLDAP remains the password store; every federated login in this lab still ends in an LDAP bind
IB-SIA-02The northgate realm, its LDAP federation, and its group import become the Identity Provider side of the trust; you also reuse the curl and jq skills
Restart and verify the Labs 01 and 02 environment
# Bring the track containers up
docker start ib-openldap ib-keycloak ib-phpldapadmin

# Confirm the northgate realm answers
curl -s http://localhost:8081/realms/northgate/.well-known/openid-configuration | jq -r '.issuer'
# Expected: http://localhost:8081/realms/northgate

# Confirm LDAP still authenticates Amina
ldapwhoami -x -H ldap://localhost:389 \
  -D "uid=asmith,ou=people,dc=identitybytes,dc=lab" -w "<USER_PASSWORD>"

Both checks must pass before you continue. If either fails, revisit the verification steps of the earlier lab; nothing in Lab 03 can compensate for a broken foundation.

3.2 System requirements

ResourceMinimumRecommended
Operating systemUbuntu 22.04, macOS 13, or Windows 11 with WSL2Ubuntu 22.04 LTS (all commands verified here)
RAM6 GB8 GB
Disk12 GB free20 GB free
CPU2 cores4 cores
NetworkNo new ports needed; everything rides the existing 8081 and 389.

3.3 Required tools and versions

ToolVersionPurpose
Docker, curl, jq, ldap-utilsAs installed in Labs 01 and 02Container control, metadata retrieval, LDAP checks
xmllint (libxml2)2.9.x or laterPretty prints and queries the XML metadata and assertions you will capture
A Chromium or Firefox browserAny current versionDeveloper tools capture the SAML Response in flight

3.4 Installation commands

Ubuntu 22.04 / Debian / Windows WSL2 Ubuntu: install xmllint
# xmllint ships in the libxml2-utils package. It is to XML what jq is
# to JSON: the identity engineer's assertion reading tool.
sudo apt-get update && sudo apt-get install -y libxml2-utils
macOS 13+: verify xmllint (built in)
which xmllint
# Expected: /usr/bin/xmllint. macOS ships libxml2; nothing to install.

3.5 Verification of tools

Verify xmllint works
echo '<a><b>ok</b></a>' | xmllint --format -

Expected output: the same XML, pretty printed across three lines with an XML declaration. Any error means libxml2-utils did not install; re-run the install command and read its output.

VERIFICATIONLabs 01 and 02 answering, xmllint formatting XML: you are ready. Keep three passwords to hand: the Keycloak admin, the LDAP user password, and the LDAP admin password.
Section 4

Real World Problem Statement

This lab solves the problem that appears the moment identity leaves one organisation's walls: cross domain trust. Mergers and acquisitions, partner portals, and above all SaaS adoption all pose the same question: how does system B, which you do not run, accept users from directory A, which you do, without ever seeing their passwords? Copying accounts fails immediately, because passwords cannot move, lifecycle events do not propagate, and the auditors who signed off on Lab 01's single source of truth would rightly object to a second one.

Federation is the answer, and SAML 2.0 is its incumbent language. One side, the Identity Provider, authenticates the user and issues a digitally signed XML assertion; the other side, the Service Provider, validates the signature against a pinned certificate and trusts the identity inside. No shared database, no password ever crossing the boundary, and trust that can be revoked by removing one certificate. OIDC, which you met in Lab 02, is the modern equivalent, but SAML predates it by a decade and remains the default federation protocol of enterprise SaaS and government platforms; a senior IAM architect who cannot read an assertion cannot debug half the SSO estate.

Why it matters, across four dimensions

Risk

Federation confines credentials to one authentication authority. A compromised Service Provider yields no passwords to steal, and revoking trust is a single certificate or client removal, not a password reset campaign across two companies.

Compliance

Access to the acquired estate flows from the acquirer's joiner and leaver process on day one, keeping ISO 27001 A.5.16 lifecycle control and audit evidence intact through the merger instead of losing it for a year of migration.

Productivity

Day one access without account migration. Every SaaS onboarding becomes a metadata exchange measured in hours, and users carry one identity across organisational boundaries.

Security Posture

Signed assertions with audience restriction and short validity windows are Zero Trust artefacts: verifiable, scoped, expiring. Federation is also the pattern behind cloud console access, which IB-SIA-30 builds on AWS.

Concrete scenario

Northgate Financial, 5,000 employees, completes the acquisition of Harborview Wealth, 400 employees, on the first of the month. The integration board mandates: Northgate staff shall access Harborview's platforms using Northgate credentials from day one; no Northgate password shall be stored or transmitted to Harborview systems; the trust shall be revocable unilaterally by either security team. Your task is the reference federation: Harborview's identity platform configured as a SAML Service Provider brokering to Northgate's Identity Provider, signature validation pinned to Northgate's published certificate, identity attributes mapped across the boundary, and the whole trust proven with a captured, decoded, signed assertion as evidence for the security review.

Section 5

Skills Mapped to Production Solutions

Skill LearnedReal World Enterprise Application
Configuring a SAML Identity Provider and Service Provider pairEvery enterprise SaaS SSO rollout: Salesforce, Workday, ServiceNow, and the AWS console all onboard through exactly this IdP and SP exchange
Reading and exchanging SAML metadata documentsThe universal onboarding artefact of B2B federation; vendor SSO forms are asking you for fields from this document
Extracting and pinning an IdP signing certificateEstablishing and rotating federation trust anchors, and the operational discipline behind certificate expiry runbooks for SSO estates
Identity brokering between realmsMulti tenant and M&A architectures: Keycloak, Okta, and Entra ID all broker external IdPs into a local session using this pattern (Entra ID calls it external identities, Okta calls it inbound federation)
Attribute mapping across a trust boundaryCarrying email, name, and entitlements into partner and SaaS applications; the SAML equivalent of Lab 02's claims engineering
Configuring first login flows for brokered usersDeciding how externally authenticated users materialise locally: auto provisioning versus account linking, a real governance decision in every federation design
Capturing and decoding SAML Responses with developer tools and xmllintFrontline federation troubleshooting: the skill behind every "SSO stopped working with vendor X" incident bridge
Negative testing signature validation and audience restrictionAssurance work: proving to security review that tampered or misdirected assertions are rejected, not assuming it
Section 6

Architecture Overview

User's Browser carries every SAML message KEYCLOAK CONTAINER ib-keycloak : 8081 (two isolated realms, two identity domains) REALM: harborview Service Provider (acquired firm) SAML Identity Broker alias: northgate-saml Pinned IdP certificate validates every signature Brokered users + attribute importers asmith provisioned on first login REALM: northgate Identity Provider (acquirer) SAML client (the SP registration) clientId = harborview entity ID RS256 signing key + SAML descriptor /protocol/saml/descriptor LDAP federation (Lab 02) READ_ONLY → ib-openldap OPENLDAP (LAB 01) the only password store anywhere LDAP bind 389 1. visit Harborview 2. AuthnRequest (redirect) 4. signed SAML Response (POST) 5. local session 3. user authenticates here (LDAP) INSIDE THE SAML RESPONSE (captured in Phase 5) <Issuer> = iss <NameID> = sub <Audience> = aud NotOnOrAfter = exp <Attribute> = claims <Signature> = JWS sig One mental model, two encodings: everything you learned about JWTs in Lab 02 maps here

Component breakdown

ComponentPurposeTechnologyDeploymentPortsKey configuration
Realm harborviewThe acquired company's identity domain; the SAML Service Provider side of the trust via identity brokeringKeycloak realm + SAML v2.0 identity provider (broker)Created in the existing ib-keycloak container8081 (shared)Broker alias northgate-saml; validates signatures against the pinned northgate certificate; first login flow provisions users locally
Realm northgateThe acquirer's identity domain; the SAML Identity Provider that authenticates users and signs assertionsKeycloak realm (from IB-SIA-02)Existing8081 (shared)Publishes IdP metadata at /realms/northgate/protocol/saml/descriptor; gains one SAML client representing Harborview
SAML client in northgateThe registration of the Service Provider: who may request assertions and where responses may be sentKeycloak SAML clientConfigured in the admin consolen/aClient ID equals the SP entity ID http://localhost:8081/realms/harborview; valid redirect URI locked to the broker endpoint; attribute mappers for email and names
OpenLDAPStill the only password store; the IdP validates every login against itosixia/openldap:1.5.0 (Lab 01)Existing389Unchanged
Browser + xmllintCapture the SAML Response in flight and decode it as evidenceDeveloper tools, base64, libxml2Hostn/aPhase 5 workflow

Data flow (SP initiated single sign-on)

  1. User visits Harborview: the browser opens the harborview account console; its login page offers the Northgate SSO option. Why: SP initiated flow, where the destination triggers federation, is how the overwhelming majority of enterprise SSO logins begin.
  2. AuthnRequest crosses the boundary: harborview redirects the browser to northgate's SAML single sign-on service carrying a SAML AuthnRequest that names the SP and where to respond. Why: SAML is front channel by design; the two platforms never talk directly, which is what lets federation cross company networks and firewalls.
  3. The IdP authenticates the user: northgate shows its login page, and the submitted password is verified by an LDAP bind through the Lab 02 federation. Why: the security perimeter of the whole federation is the IdP's authentication, which is why MFA in IB-SIA-05 lands here and instantly protects every SP downstream.
  4. A signed Response returns: northgate posts a SAML Response to harborview's Assertion Consumer Service, the broker endpoint, containing an assertion with the subject, validity conditions, audience restriction, attributes, and an XML signature over the whole document. Why: the signature and pinned certificate are the entire basis of trust; nothing else about the message is trustworthy on its own.
  5. The SP validates and establishes a session: harborview verifies the signature, checks issuer, audience, and time conditions, runs the first login flow to provision or link the local user, imports mapped attributes, and issues its own session. Why: this local session is what makes the user "logged in" to the acquired estate, and its lifetime is now a harborview policy decision, a subtlety that matters in incident response.

Security considerations

ControlIn this lab
Trust anchoringSignature validation is switched on at the SP and pinned to the certificate you extract from northgate's metadata; the negative tests break and restore it deliberately
Audience and destination restrictionThe assertion carries an AudienceRestriction naming the SP entity ID, and the IdP will only post responses to the registered broker endpoint
Replay and time windowsAssertions carry NotBefore and NotOnOrAfter conditions and one time IDs; Keycloak rejects stale or reused responses
Credential confinementPasswords exist only in OpenLDAP and are only ever entered on the IdP's page; the SP realm provably stores none (verified in Section 8)
AuditLogin events in both realms record the brokered flow from each side, giving the two security teams independent evidence
Section 7

Step by Step Implementation

Phase 1: Create the Harborview Realm (the Service Provider side)

Step 1.1: Create the realm and enable its event log

Purpose

Stand up the acquired company's identity domain as an isolated realm.

Context

Running both domains as realms in one Keycloak container is a lab economy, not an architectural statement: realms share nothing, users, keys, sessions, and configuration are fully isolated, so the trust you build between them is the same trust you would build between two companies' separate platforms. Your Lab 02 negative test N3 already proved this isolation empirically.

Actions in the admin console
  1. Log in to http://localhost:8081 as admin.
  2. Realm selector, Create realm, name harborview (lower case, exactly), Create.
  3. Under Realm settings → Events, switch on saving of user events, as you did for northgate, so the brokered logins leave a trail.
VERIFICATIONRun curl -s http://localhost:8081/realms/harborview/.well-known/openid-configuration | jq -r '.issuer'. Expected: http://localhost:8081/realms/harborview. The realm exists and publishes metadata.

Phase 2: Establish Trust, Direction One: Teach Harborview About Northgate

Step 2.1: Fetch northgate's IdP metadata and extract the signing certificate

Purpose

Retrieve the IdP's published SAML metadata and pull out the X.509 signing certificate that will anchor the trust.

Context

Every SAML party publishes a metadata document describing its entity ID, endpoints, bindings, and certificates. In real onboarding, this document, or a form asking for its fields, is what a vendor sends you. We read it with curl and xmllint rather than clicking an import button, because the day an import fails, and it will, the engineer who can read metadata raw is the one who fixes it. One environment note before you start: this metadata contains URLs built from the requesting hostname, and all browser traffic in this lab uses localhost:8081, so we fetch from the host and configure endpoints manually rather than using Keycloak's server side import from URL, which inside the container would resolve localhost to the wrong place. Recognising which URLs are evaluated by a server and which by a browser is a genuine federation skill; misconfigured hostnames cause a large share of real world SAML incidents.

Fetch the metadata and extract the certificate
# Create a workspace for this lab's artefacts
mkdir -p ~/ib-sia-03 && cd ~/ib-sia-03

# Fetch the northgate realm's SAML IdP metadata (the descriptor)
curl -s http://localhost:8081/realms/northgate/protocol/saml/descriptor \
  -o northgate-idp-metadata.xml

# Read it, pretty printed. Note three things as you scroll:
#   entityID          the IdP's identity in every assertion's Issuer
#   SingleSignOnService  the URL AuthnRequests go to
#   X509Certificate   the public key that verifies signatures
xmllint --format northgate-idp-metadata.xml | less

# Extract the certificate value into a file. The XPath below pulls the
# first X509Certificate element's text, and tr strips whitespace.
xmllint --xpath "//*[local-name()='X509Certificate'][1]/text()" \
  northgate-idp-metadata.xml | tr -d '[:space:]' > northgate-signing.cert

# Sanity check: a base64 blob several hundred characters long
wc -c northgate-signing.cert
Expected outcome

The metadata file saved, and northgate-signing.cert containing roughly 800 to 1200 characters of base64.

VERIFICATIONDecode the certificate and read its subject: base64 -d northgate-signing.cert | openssl x509 -inform DER -noout -subject -dates. Expected: a subject containing CN=northgate (wrap the base64 in PEM headers first if your openssl build insists: it will still show the northgate CN). If the xpath command returned nothing, the descriptor URL was wrong; re-check the realm name in the URL.
What just happened?You performed the receiving half of a federation onboarding: read a partner's metadata and captured their trust anchor. The certificate file now on disk is the single artefact that will let Harborview distinguish a genuine Northgate assertion from a forged one.

Step 2.2: Configure the SAML identity provider (broker) in harborview

Purpose

Point the harborview realm at northgate's SAML endpoints, switch on signature validation, and pin the extracted certificate.

Actions in the admin console (realm: harborview)
  1. Go to Identity providers in the left menu and choose SAML v2.0.
  2. Alias northgate-saml (this becomes part of URLs, so type it exactly). Display name Northgate SSO.
  3. SAML entity descriptor: leave empty (we configure manually for the hostname reason above). Set Identity provider entity ID to http://localhost:8081/realms/northgate.
  4. Single Sign-On service URL http://localhost:8081/realms/northgate/protocol/saml.
  5. NameID policy format Username. HTTP-POST binding response ON. HTTP-POST binding for AuthnRequest ON. Want AuthnRequests signed OFF (the hardening table in Section 9 turns this on with proper SP keys).
  6. Validate signatures ON. In Validating X509 certificates, paste the entire contents of northgate-signing.cert (one long base64 line; cat ~/ib-sia-03/northgate-signing.cert prints it for copying).
  7. Click Add (or Save).
  8. On the saved provider, note two generated values you will need in Phase 3: the Redirect URI shown at the top, http://localhost:8081/realms/harborview/broker/northgate-saml/endpoint, which is the Assertion Consumer Service, and the Service provider entity ID field, which defaults to http://localhost:8081/realms/harborview.
Expected outcome

The provider appears under Identity providers, and opening a private window at http://localhost:8081/realms/harborview/account now shows a Northgate SSO button on the login page. Do not click it yet; the other half of the trust does not exist, and the next phase creates it.

INFOVocabulary anchor: in SAML the application side is the Service Provider (SP), the login side is the Identity Provider (IdP), and the SP endpoint that receives responses is the Assertion Consumer Service (ACS). Keycloak's brokering wraps the SP role: it consumes the external assertion, then acts as the local realm's session authority.

Phase 3: Establish Trust, Direction Two: Register Harborview at Northgate

Step 3.1: Create the SAML client in the northgate realm

Purpose

Register the Service Provider at the IdP: who Harborview is (entity ID) and the only place responses may be sent (the ACS).

Context

Trust in SAML is mutual and explicit. Phase 2 taught the SP to recognise the IdP's signature; this phase teaches the IdP which SP may ask for assertions and where they may travel. The client ID of a Keycloak SAML client is not a friendly name: it must equal the SP's entity ID character for character, because the IdP matches incoming AuthnRequests by their Issuer element. Getting this wrong produces the single most common SAML setup error, and you will trigger it deliberately in Section 8.

Actions in the admin console (switch the realm selector to northgate)
  1. Go to Clients, Create client.
  2. Client type SAML. Client ID http://localhost:8081/realms/harborview (the SP entity ID from Step 2.2, exactly). Name Harborview Wealth Federation. Click Next, then Save.
  3. On the client's Settings tab: Valid redirect URIs http://localhost:8081/realms/harborview/broker/northgate-saml/endpoint. Name ID format username. Force POST binding ON.
  4. Open the Keys tab and switch Client signature required OFF. Why: the broker is not signing its AuthnRequests in this lab (Step 2.2 point 5), and with this on, the IdP would reject every unsigned request. Section 9 restores mutual signing as production hardening.
  5. Under Signature and Encryption on the Settings tab, confirm Sign documents is ON and additionally switch Sign assertions ON, so the assertion itself carries a signature you will inspect in Phase 5. Click Save.
VERIFICATIONThe client list in northgate now shows the harborview entity ID with protocol saml. Configuration cannot be fully verified until a login runs end to end, which is Phase 4; SAML's trust is only observable in motion.

Step 3.2: Map identity attributes into the assertion

Purpose

Add mappers so the assertion carries email, given name, and surname, the SAML counterpart of Lab 02's claims engineering.

Actions in the admin console (realm: northgate)
  1. Open Clients → http://localhost:8081/realms/harborview → Client scopes and click the dedicated scope (its name ends in -dedicated).
  2. Click Add mapper → By configuration and create three User Property mappers, one at a time:
  3. Name email, Property email, SAML Attribute Name email, SAML Attribute NameFormat Basic. Save.
  4. Name firstName, Property firstName, SAML Attribute Name firstName, NameFormat Basic. Save.
  5. Name lastName, Property lastName, SAML Attribute Name lastName, NameFormat Basic. Save.
Context

On the harborview side, Keycloak's broker automatically consumes attributes named email, firstName, and lastName into the corresponding user fields, which is why we chose those names. For any other attribute you would add an Attribute Importer mapper on the broker; you will do exactly that for groups in a stretch exercise at the end of Section 8.

Phase 4: The First Cross Domain Login

Step 4.1: Log in to Harborview with a Northgate identity

Purpose

Run the complete SP initiated flow as Amina Smith and watch a user materialise in a realm that has never seen her password.

Actions
  1. Open a fresh private browser window and go to http://localhost:8081/realms/harborview/account.
  2. On the login page, click Northgate SSO.
  3. You land on the northgate login page (read the URL bar and confirm the realm changed). Sign in as asmith with the Lab 01 <USER_PASSWORD>.
  4. Keycloak's default first login flow presents an Update Account Information page pre-filled from the mapped attributes: email, first name, last name from LDAP via the assertion. Click Submit.
Expected outcome

The harborview account console loads, signed in as Amina Smith, personal information populated. She authenticated in one domain and is working in another.

VERIFICATIONThree independent proofs in the admin console. One: in realm harborview → Users, asmith now exists, and her Identity provider links tab shows northgate-saml with her external identity. Two: in harborview → Users → asmith → Credentials, no password is stored, so the SP realm holds no secret to steal. Three: in realm northgate → Events, a LOGIN event records the authentication with client equal to the harborview entity ID. A failure at the click of Northgate SSO with an Invalid Requester or invalid_redirect_uri style error means the client ID or redirect URI in Step 3.1 does not exactly match; character by character comparison resolves it.
PRODUCTION CONSIDERATIONThe Update Account Information page is the default First login flow behaviour, and its handling is a real governance decision: automatic provisioning (as here) suits M&A speed, while account linking with verification suits environments where SP side accounts already exist and hostile pre-registration is a risk. Review the flow under Authentication → First broker login and know what each step does before an auditor asks.
What just happened?Amina's browser carried three messages across a trust boundary: an AuthnRequest naming the SP, her credentials to the IdP only, and a signed Response back to the ACS. Harborview verified the signature against the certificate you pinned in Step 2.2, checked audience and time conditions, provisioned a local linked account, and issued its own session. The password touched one system, the one you built in Lab 01.

Phase 5: Capture and Dissect the Assertion

Step 5.1: Capture the SAML Response with browser developer tools

Purpose

Intercept the actual signed Response as it crosses the boundary, the raw evidence of everything Phase 4 claimed.

Actions
  1. Open a new private window, press F12 to open developer tools, and select the Network tab. Tick Preserve log, because the flow crosses several redirects.
  2. Repeat the login: http://localhost:8081/realms/harborview/account, Northgate SSO, sign in as jpatel this time (a fresh first login for the second user).
  3. In the network list, find the POST request to a URL ending in /broker/northgate-saml/endpoint.
  4. Open its Payload (Chrome) or Request (Firefox) tab, locate the form field named SAMLResponse, right click its value and copy it. In Chrome, use the view decoded toggle if the value shows URL encoded characters such as %2B; you want the plain base64 that starts with letters like PHNhbWxwOl....
  5. Save it to a file: run nano ~/ib-sia-03/samlresponse.b64, paste as one line, save and exit (Ctrl O, Enter, Ctrl X).
INFOProfessionals often use the SAML-tracer browser extension for this capture, and you should install it eventually; we use raw developer tools first because they exist on every machine you will ever troubleshoot from, including the locked down ones.

Step 5.2: Decode and read the assertion

Purpose

Turn the captured blob into readable XML and walk its security relevant elements one by one.

Decode the SAMLResponse and pretty print it
# Decode base64 into XML and format it. If your paste was URL encoded,
# the python line below handles both decodings in one pass.
base64 -d ~/ib-sia-03/samlresponse.b64 | xmllint --format - \
  > ~/ib-sia-03/samlresponse.xml || \
python3 -c "import sys,base64,urllib.parse;d=open('/home/'+__import__('getpass').getuser()+'/ib-sia-03/samlresponse.b64').read().strip();print(base64.b64decode(urllib.parse.unquote(d)).decode())" \
  | xmllint --format - > ~/ib-sia-03/samlresponse.xml

# Read it
less ~/ib-sia-03/samlresponse.xml
Expected outcome (abridged, your IDs and timestamps will differ)
<samlp:Response Destination="http://localhost:8081/realms/harborview/broker/northgate-saml/endpoint" ...>
  <saml:Issuer>http://localhost:8081/realms/northgate</saml:Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
  <saml:Assertion ...>
    <saml:Issuer>http://localhost:8081/realms/northgate</saml:Issuer>
    <dsig:Signature> ... <X509Certificate>MIICnzCC...</X509Certificate> ... </dsig:Signature>
    <saml:Subject>
      <saml:NameID Format="...:unspecified">jpatel</saml:NameID>
    </saml:Subject>
    <saml:Conditions NotBefore="2026-07-06T09:14:02Z" NotOnOrAfter="2026-07-06T09:15:02Z">
      <saml:AudienceRestriction>
        <saml:Audience>http://localhost:8081/realms/harborview</saml:Audience>
      </saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AttributeStatement>
      <saml:Attribute Name="email"><saml:AttributeValue>jay.patel@identitybytes.lab</saml:AttributeValue></saml:Attribute>
      <saml:Attribute Name="firstName"><saml:AttributeValue>Jay</saml:AttributeValue></saml:Attribute>
      <saml:Attribute Name="lastName"><saml:AttributeValue>Patel</saml:AttributeValue></saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>
VERIFICATIONFive reading checks, each with its Lab 02 twin: Issuer equals the northgate realm URL (iss), NameID is jpatel (sub), Audience equals the harborview entity ID (aud), NotOnOrAfter sits about sixty seconds after issue (exp, and notice how much shorter than the JWT's five minutes: SAML assertions are consumed once, immediately), and the Signature block contains the same certificate you pinned, which xmllint --xpath "//*[local-name()='X509Certificate'][1]/text()" ~/ib-sia-03/samlresponse.xml | tr -d '[:space:]' | diff - ~/ib-sia-03/northgate-signing.cert proves: no output means identical.
What just happened?You read a live federation credential the way an SP's validation code does. Every SSO incident bridge you will ever join comes down to one of the five elements you checked: wrong issuer, wrong audience, clock skew breaking the conditions window, an unexpected NameID, or a signature that fails against the pinned certificate. You now know where each lives and what healthy looks like.
Section 8

Testing and Validation

End to end scenario: single sign-on across the merged estate

This validates the promise made to the integration board: one login, both domains, one password store, revocable trust.

Run the end to end validation
# STAGE 1: fresh private window, log in to harborview via Northgate SSO
# as asmith. Expected: harborview account console, as in Phase 4.

# STAGE 2: single sign-on proof. In the SAME window, open
# http://localhost:8081/realms/northgate/account
# Expected: signed in WITHOUT a password prompt. The IdP session from
# Stage 1 is live, which is precisely what SSO means: one authentication,
# many destinations.

# STAGE 3: source of truth proof. Change Amina's password in LDAP:
ldappasswd -x -H ldap://localhost:389 \
  -D "cn=admin,dc=identitybytes,dc=lab" -w "<YOUR_ADMIN_PASSWORD>" \
  -s "<NEW_USER_PASSWORD>" "uid=asmith,ou=people,dc=identitybytes,dc=lab"
# Then, in a NEW private window, log in to harborview via Northgate SSO.
# Expected: the old password fails, the new one succeeds, and neither
# Keycloak realm was touched. Two federation hops, one enforcement point.

# STAGE 4: revocation drill. In realm northgate, open the harborview
# client and switch Enabled OFF, Save. In a new private window, attempt
# the SSO login. Expected: northgate refuses the AuthnRequest with an
# error page; the trust is severed by one toggle. Re-enable and confirm
# login works again. This is the unilateral revocation the integration
# board required, demonstrated.

Negative tests

Run the negative tests
# TEST N1: tampered trust anchor must break the federation.
# In realm harborview, Identity providers, northgate-saml: replace the
# validating certificate with the WRONG one. Generate a convincing decoy:
openssl req -x509 -newkey rsa:2048 -nodes -keyout /tmp/fake.key \
  -out /tmp/fake.crt -days 1 -subj "/CN=northgate" 2>/dev/null
grep -v "CERTIFICATE" /tmp/fake.crt | tr -d '\n'; echo
# Paste that base64 into Validating X509 certificates, Save, then attempt
# the SSO login in a fresh private window.
# Expected: login FAILS at the broker with an invalid signature error
# (northgate's events show success; harborview's show IDENTITY_PROVIDER
# login_error). Same CN, wrong key: names mean nothing, signatures mean
# everything. RESTORE the real certificate from
# ~/ib-sia-03/northgate-signing.cert and confirm login works again.

# TEST N2: unregistered Service Provider must be refused.
# In realm northgate, open the harborview SAML client and change the
# Client ID to http://localhost:8081/realms/harborview-WRONG, Save.
# Attempt the SSO login. Expected: northgate rejects the AuthnRequest
# (Invalid Requester style error) because no client matches the request's
# Issuer. Change the Client ID back and confirm recovery. This is the
# most common real world SAML fault, now seen from the inside.

# TEST N3: response replay must fail.
# After a successful login, use the browser back button to return to the
# POST and resubmit it (the browser will ask to resend the form data).
# Expected: an error page, not a second session. Assertions are single
# use and time boxed; the sixty second NotOnOrAfter you read in Phase 5
# is doing exactly this job.

Stretch exercise: carry groups across the boundary

Add a Group list mapper to the SAML client in northgate (SAML Attribute Name groups, single attribute ON, full path OFF), and an Attribute Importer mapper on the harborview broker mapping attribute groups to a user attribute of the same name. Log in again, re-capture the Response, and find finance-team inside an AttributeStatement, your Lab 01 LDIF group having now crossed a federation boundary. This is the mechanism behind role based access in federated SaaS.

Common failure modes and solutions

SymptomLikely causeSolution
Invalid Requester error at northgate when clicking Northgate SSOSAML client ID does not exactly equal the SP entity IDCompare the client ID in northgate against the Service provider entity ID shown on the harborview broker, character by character
Invalid redirect or invalid ACS errorValid redirect URIs on the SAML client does not match the broker endpointPaste the exact Redirect URI displayed on the broker configuration page
Broker reports invalid signature on every loginWrong, truncated, or whitespace damaged certificate pasted at the SP, or Sign documents disabled at the IdPRe-extract with the Step 2.1 command, paste as one unbroken line, and confirm Sign documents is ON for the client
IdP rejects the AuthnRequest as unsigned or badly signedClient signature required left ON while the broker sends unsigned requestsAlign both sides: for the lab, Client signature required OFF; for production, ON with the SP certificate imported (Section 9)
Login loops back to the harborview login pageThird party cookie or mixed private window contaminationUse one fresh private window per test; close all others for the realm
Conditions error, assertion not yet valid or expiredClock skew between issuing and consuming systems (impossible in this one container lab, endemic in real estates)In production, enforce NTP everywhere and know that SAML tolerances are seconds, not minutes
Update Account Information page shows empty fieldsAttribute mappers missing at the IdP client, or names not matching email, firstName, lastNameRecheck Step 3.2 mapper names; capture the Response and confirm the AttributeStatement actually carries values
Section 9

Security Analysis

What makes this implementation secure

What is intentionally simplified for the lab

Production hardening recommendations

RecommendationWhyCovered in
TLS on every endpoint with certificates from a managed CASAML's protections assume a confidential channel; assertions over HTTP are interceptable regardless of signaturesIB-SIA-17 to 19 (PKI phase)
Mutual signing: SP signs AuthnRequests, IdP enforces Client signature required, and consider assertion encryption for sensitive attributesPrevents request forgery and shields attribute contents from the browser channelHardening exercise on this build; keys via the PKI phase
Operational certificate rotation runbook with overlapping validity and calendar alertsExpired federation certificates are a leading cause of total SSO outage; rotation is a rehearsed procedure, not an emergencyIB-SIA-22 (certificate lifecycle automation)
Enforce MFA at the IdPThe IdP is now the single door to two estates; NIST SP 800-63B AAL2 and every cyber insurance questionnaire expect a second factor on itIB-SIA-05
Harden the first broker login flow: email domain allow listing, verification, or manual review for sensitive SPsAuto provisioning trades control for speed; the right setting depends on what the SP protectsIB-SIA-08 (adaptive authentication)
Configure and test Single Logout, and document its limitsSession lifetime after IdP logout is an incident response question you must be able to answer preciselyIB-SIA-12 (session management controls)
Alert on IDENTITY_PROVIDER login errors and signature validation failures in the SP's eventsA spike is either an outage or an attack, and both deserve minutes, not daysIB-SIA-35 (identity observability)
Section 10

Cleanup Instructions

Option A: pause the lab, keep everything (recommended, later labs reuse both realms)

Stop containers without losing data
docker stop ib-keycloak ib-openldap ib-phpldapadmin
# Resume later with:
docker start ib-openldap ib-keycloak ib-phpldapadmin

Option B: remove Lab 03 only, keep Labs 01 and 02

Remove the federation, preserve everything else
# All Lab 03 state lives inside Keycloak configuration, so removal is
# three admin console actions rather than container commands:
#   1. Realm northgate, Clients: delete the harborview entity ID client
#   2. Realm harborview, Identity providers: delete northgate-saml
#   3. Realm selector: delete the harborview realm entirely
#      (Realm settings, Action menu, Delete)

# Remove the captured artefacts from disk, keeping the metadata and
# certificate if you are publishing the portfolio first:
rm -f ~/ib-sia-03/samlresponse.b64 ~/ib-sia-03/samlresponse.xml /tmp/fake.key /tmp/fake.crt
VERIFICATIONAfter Option B: curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8081/realms/harborview/.well-known/openid-configuration returns 404, while the same check for northgate still returns 200 and a Lab 02 token request still succeeds. The captured SAMLResponse files contain a real user identifier and session artefacts, so they never enter the portfolio repository unredacted.
Section 11

Recommended Learning Links

Section 12 (Bonus)

Portfolio Publishing Guide: Evidence of a Trust You Built and Broke

Lab 03's portfolio value is different from the first two: the artefact is not a deployment but a trust relationship, and the evidence that sells it is the dissected assertion plus the negative tests. Interviewers meet many candidates who have configured SSO; they meet few who have deliberately broken signature validation with a decoy certificate and can narrate why it failed.

12.1 Prepare sanitised artefacts

Redact and collect the evidence
cd ~/identity-bytes-architect-labs
mkdir -p lab-03-saml-federation/{docs,config,evidence,screenshots}

# The IdP metadata and signing certificate are public by design
# (any browser can fetch the descriptor), so they publish as-is:
cp ~/ib-sia-03/northgate-idp-metadata.xml lab-03-saml-federation/config/
cp ~/ib-sia-03/northgate-signing.cert lab-03-saml-federation/config/

# The captured Response needs redaction before it becomes evidence.
# Blank the signature and cut identifiers, keeping the structure that
# demonstrates your analysis:
sed -E 's/(<dsig:SignatureValue>)[^<]+/\1REDACTED/; s/(ID=")[^"]+/\1REDACTED/g; s/(SessionIndex=")[^"]+/\1REDACTED/g' \
  ~/ib-sia-03/samlresponse.xml > lab-03-saml-federation/evidence/samlresponse.redacted.xml

# Confirm nothing sensitive survived. Expected: no output.
grep -nE "SignatureValue>[A-Za-z0-9+/]" lab-03-saml-federation/evidence/samlresponse.redacted.xml
SECURITY WARNINGNever publish an unredacted SAMLResponse, even from a lab: it normalises a habit that will one day leak a production assertion containing live session identifiers and PII. Redaction discipline is part of the skill being demonstrated.

12.2 Add Lab 03 to the repository and push

Create the README, commit, and push
cat > lab-03-saml-federation/README.md << 'EOF'
# Lab 03: SAML 2.0 Federation and Identity Brokering

Part of my Identity Bytes Senior IAM Architect lab series (IB-SIA-03).
Builds on Labs 01 and 02: the northgate realm (backed by OpenLDAP)
becomes a SAML Identity Provider trusted by a second identity domain.

## Problem this solves
Cross domain trust after an acquisition: day one access to the acquired
estate using acquirer credentials, no account migration, no password
ever crossing the boundary, and trust either side can revoke with one
change.

## What I built
- A second Keycloak realm (harborview) acting as SAML Service Provider
  via identity brokering
- Manual metadata exchange in both directions, with the IdP signing
  certificate extracted by XPath and pinned at the SP
- SP registration at the IdP with entity ID and ACS locked down
- Attribute mapping (email, names) across the trust boundary, plus a
  group attribute in the stretch exercise
- Live capture of the SAML Response in browser developer tools and a
  full element by element analysis (Issuer, NameID, Conditions,
  AudienceRestriction, AttributeStatement, Signature)

## What I broke on purpose
- Signature validation, using a decoy self signed certificate with the
  same CN: federation failed exactly as it should
- SP registration, by mutating the entity ID: the IdP refused the
  AuthnRequest
- Replay, by resubmitting a consumed Response: rejected

## Skills demonstrated
SAML 2.0 protocol fluency, federation onboarding and metadata literacy,
trust anchor management, identity brokering, first login flow
governance, front channel troubleshooting, and negative security
testing.

Full step by step guide: docs/IB-SIA-03-saml-federation.html
Redacted assertion evidence: evidence/samlresponse.redacted.xml
EOF

cp <PATH_TO>/IB-SIA-03-saml-federation.html lab-03-saml-federation/docs/

# Screenshots worth taking: the harborview login page with the
# Northgate SSO button, the Identity provider links tab on the brokered
# user, and the formatted assertion in your terminal.

git add .
git commit -m "Lab 03: SAML 2.0 federation, IdP to SP brokering with pinned trust anchor, assertion dissection, negative tests"
git push

# Update the track index in the root README:
# | 03 | SAML 2.0 federation, brokering, assertion analysis | Complete |
VERIFICATIONThe repository now shows three labs telling one continuous story: directory, then IdP, then federation. Check the rendered redacted XML on GitHub; if it displays as one line, add a .xml friendly view by keeping the pretty printed version, which you already have.

12.3 Share it on LinkedIn

Attach the terminal screenshot of the formatted assertion with the Signature block visible (redacted), because XML that people usually only fear, presented calmly annotated, stops the scroll of exactly the audience you want. GitHub link in the first comment. A draft in the Identity Bytes style:

Draft post:

I forged a certificate this weekend. Same common name as the real one, freshly generated, planted straight into a federation trust store. The single sign-on broke instantly, and that failure was the most reassuring thing I have seen all month.

Here is the situation that led there. In my lab series, one company has acquired another, and the integration requirement is the one every merger produces: day one access to the acquired estate with acquirer credentials, no account migration, no passwords crossing the boundary. The protocol that still answers this in most enterprises is SAML 2.0, the same one behind your Salesforce, Workday, and AWS console logins.

So I built the trust properly. Extracted the identity provider's signing certificate from its metadata by hand, pinned it at the service provider, registered the entity IDs and assertion consumer endpoints on both sides, then captured the live SAML Response in browser developer tools and read it element by element. Issuer, NameID, a sixty second validity window, an audience restriction naming exactly one consumer, and a signature over all of it.

Then I attacked my own build. The decoy certificate failed signature validation. A mutated entity ID was refused before authentication even began. A replayed response bounced off the single use conditions. Every rejection was the architecture keeping its promises.

The insight I keep returning to: federation trust is not a setting, it is a certificate, and everything else is choreography around proving possession of a key. Names, display labels, even matching common names mean nothing to the validator. This is also why the scariest line in any SSO estate is a federation certificate's expiry date.

Lab three of thirty six in the senior IAM architecture series I am publishing. Guide, metadata artefacts, and the redacted assertion are on my GitHub, link in the comments.

For those running federated estates: when did you last rehearse your federation certificate rotation, rather than react to it?

Same cadence mechanics as before: Tuesday to Thursday morning UK time, replies within two hours, hashtags at the end (#IAM #SAML #Federation #Keycloak #CyberSecurity), and a comment linking Labs 01 and 02 so the series compounds.

12.4 Interview leverage from this lab

This lab evidences the federation and SSO lines of the target role directly, and it arms you for the classic architecture interview question, SAML or OIDC and why, with something better than a comparison table: you have decoded both credentials, mapped Issuer to iss and Audience to aud from your own captures, and can discuss where each protocol's failure modes live. When the follow up question is about certificate rotation or clock skew, you will be describing incidents you have already caused, on purpose, in an environment you built.