SAML Authentication Workflow & Integration Guide¶
Overview¶
This document outlines the complete SAML (Security Assertion Markup Language) authentication workflow integrated with the Bank USSD platform. SAML provides Single Sign-On (SSO) capabilities complementing LDAP authentication.
Architecture¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Bank USSD Application β
β (Service Provider - SP) β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Authentication Routes β β
β β β’ POST /auth/saml/login β Initiate SSO β β
β β β’ POST /auth/saml/acs β Handle SAML Response β β
β β β’ GET /auth/saml/logout β Initiate Logout β β
β β β’ POST /auth/saml/sls β Handle Logout Responseβ β
β β β’ GET /auth/saml/metadata β SP Metadata β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β² β
β β β
β SAML Assertions β SAML Requests β
β β β
ββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ
β
ββββββββββββββββ΄βββββββββββββββ
β β
ββββββββΌββββββββββ ββββββββΌβββββββββββββ
β Keycloak β β SAML Response β
β IdP Server βββββββββββΊβ ACS Callback β
β (Optional) β β β
ββββββββββββββββββ βββββββββββββββββββββ
β
βββββββββ΄βββββββββ
β β
ββββββΌβββββ βββββββΌβββββββ
β LDAP β β Database β
β Server β β (User β
β β β Auth) β
βββββββββββ βββββββββββββββ
Key Components¶
1. Service Provider (SP) Configuration¶
Location: Bank USSD Backend (/api/v1/auth)
Configuration stored in:
- Database: auth.saml_config table
- Environment: SAML_* env variables
- Runtime: controller.auth._saml_config()
Key Settings:
{
"sp_entity_id": "http://localhost/api/v1/auth/saml/metadata",
"acs_url": "http://localhost/api/v1/auth/saml/acs",
"slo_url": "http://localhost/api/v1/auth/saml/sls",
"idp_entity_id": "http://keycloak:8080/auth/realms/bank-ussd",
"idp_sso_url": "http://keycloak:8080/auth/realms/bank-ussd/protocol/saml",
"idp_x509cert": "..certificate...",
"nameid_format": "persistent"
}
2. Identity Provider (IdP) Configuration¶
Using: Keycloak (SAML 2.0 compliant)
Realm: bank-ussd
Realm URL: http://keycloak:8080/auth/realms/bank-ussd
SAML Endpoint: http://keycloak:8080/auth/realms/bank-ussd/protocol/saml
Client Configuration:
- Client ID: bank-ussd-saml
- Protocol: SAML
- Valid Redirect URIs:
- http://localhost:8000/api/v1/auth/saml/acs
- http://localhost/api/v1/auth/saml/sls
SAML Authentication Flow¶
Phase 1: Initiation¶
User β Browser: Click "Login with SAML"
βΌ
[POST /auth/saml/login] OR [Redirect to IdP directly]
βΌ
Backend creates AuthnRequest
βΌ
Browser redirected to Keycloak (IdP)
Phase 2: Authentication at IdP¶
Keycloak displays login form
βΌ
User enters credentials (username/password)
βΌ
Keycloak validates credentials
βΌ
Keycloak creates SAMLResponse (signed)
βΌ
Browser POST to ACS callback
Phase 3: ACS (Assertion Consumer Service)¶
[POST /auth/saml/acs]
βΌ
Backend extracts SAMLResponse from POST data
βΌ
Parse and validate SAML assertion:
β’ Check signature (IdP certificate)
β’ Verify recipient ACS URL
β’ Check NotBefore/NotOnOrAfter conditions
β’ Extract NameID and attributes
βΌ
Extract user attributes:
β’ email
β’ firstName/lastName
β’ branch (custom attribute)
β’ roles (SAML roles)
βΌ
JIT Provisioning:
ββ User not in DB?
β ββ create_ldap_or_saml_user()
β ββ Create User record
β ββ Create Profile/Email/Phone
β ββ Link to Entity (by branch)
β ββ Assign roles from SAML
ββ User exists?
ββ update_user_from_saml()
ββ Update attributes
ββ Sync roles if changed
βΌ
Create session & return user
Phase 4: Logout Flow¶
[GET /auth/saml/logout]
βΌ
Create LogoutRequest
βΌ
Redirect to Keycloak SLO endpoint
βΌ
Keycloak processes logout
βΌ
Keycloak redirects to SLS callback
βΌ
[POST /auth/saml/sls]
βΌ
Validate LogoutResponse
βΌ
Clear local session
SAML User Provisioning (JIT)¶
Process:¶
create_ldap_or_saml_user(
db=database,
email="john.smith@bank.local",
username="john.smith",
idp="SAML", # Identity provider type
groups=["staff"], # SAML roles
branch="london", # From SAML attribute
kyc_data={
"first_name": "John",
"last_name": "Smith",
"phone": "+447000000000"
}
) β User
Steps:¶
-
Create User Record
-
Create Profile
-
Create Email
-
Link to Entity (Branch)
- Extract branch from SAML attribute
- Query Entity by code (case-insensitive):
Entity.code == 'london' -
Create UserEntity link:
-
Create UserIdentityProvider Link
-
Assign Roles from SAML Groups
- Map SAML roles to database roles:
adminβLDAP Admin Role - Create UserRole records:
Environment Configuration¶
Add to .env:
# SAML Settings
SAML_ENABLED=1
SAML_SP_ENTITY_ID=http://localhost/api/v1/auth/saml/metadata
SAML_ACS_URL=http://localhost/api/v1/auth/saml/acs
SAML_SLS_URL=http://localhost/api/v1/auth/saml/sls
# Keycloak IdP URLs (initially blank, will be configured in DB)
SAML_IDP_ENTITY_ID=http://keycloak:8080/auth/realms/bank-ussd
SAML_IDP_SSO_URL=http://keycloak:8080/auth/realms/bank-ussd/protocol/saml
SAML_IDP_X509CERT=
Startup Commands¶
1. Start Keycloak (Docker Compose)¶
cd deployment/
docker-compose -f docker-compose-keycloak.yaml up -d
# Wait for Keycloak to be healthy
docker logs -f keycloak
2. Get Keycloak Certificate¶
# Extract X509 certificate from Keycloak
openssl x509 -in keycloak.crt -text
# Store in database or environment
3. Configure Backend SAML¶
# Option A: Via SQL (set IdP config)
UPDATE auth.saml_config
SET
idp_entity_id = 'http://keycloak:8080/auth/realms/bank-ussd',
idp_sso_url = 'http://keycloak:8080/auth/realms/bank-ussd/protocol/saml',
saml_enabled = true
WHERE id = 1;
# Option B: Via API endpoint (if available)
POST /api/v1/auth/saml/config
{
"idp_entity_id": "http://keycloak:8080/auth/realms/bank-ussd",
"idp_sso_url": "http://keycloak:8080/auth/realms/bank-ussd/protocol/saml",
"idp_x509cert": "..cert.."
}
4. Verify SAML Metadata¶
Test Users (Keycloak)¶
| Username | Password | Role | Branch | |
|---|---|---|---|---|
| admin.keycloak | admin123 | admin | london | admin@keycloak.local |
| john.smith | password123 | staff | london | john.smith@bank.local |
| sarah.jones | password123 | customers | manchester | sarah.jones@bank.local |
| bob.wilson | password123 | operators | newcastle | bob.wilson@bank.local |
| emma.davis | password123 | agents | birmingham | emma.davis@bank.local |
Testing SAML Flow¶
1. Access SAML Login¶
# Option A: Web UI
http://localhost:5173/login?method=saml
# Option B: Direct API
curl -X POST http://localhost:8000/api/v1/auth/saml/login \
-H "Content-Type: application/json" \
-d '{
"username": "john.smith",
"channel": "web"
}'
2. Keycloak Admin Console¶
3. Verify User Provisioning¶
-- Check if user was provisioned
SELECT u.id, u.email, ue.entity_id, r.name
FROM users.users u
LEFT JOIN users.users_entity ue ON u.id = ue.user_id
LEFT JOIN users.user_roles ur ON u.id = ur.user_id
LEFT JOIN users.roles r ON ur.role_id = r.id
WHERE u.email LIKE '%@bank.local%';
Troubleshooting¶
Issue: SAML Response validation fails¶
Solution: - Verify IdP certificate is correct - Check ACS URL matches exactly - Validate assertion signature
Issue: User attributes not extracted¶
Solution:
- Verify attribute names in Keycloak mapper match SAML protocol mappers
- Check protocolMappers in realm config
- Ensure attributes are included in SAMLResponse
Issue: Branch not found, user not linked to entity¶
Solution: - Verify Entity records exist with matching codes (london, manchester, etc.) - Extract branch from SAML assertion: Check attribute name in SAMLResponse - Seed entities if missing:
Issue: Roles not assigned¶
Solution:
- Verify role mapping in LDAP_GROUP_ROLE_MAP environment variable
- Ensure Keycloak user has realm roles assigned
- Check proto col mappers include role list mapper
Integration with LDAP¶
Both LDAP and SAML support the same: - Branch extraction and entity linking - Role mapping to database roles - JIT user provisioning - KYC data capture
Differences: | Aspect | LDAP | SAML | |--------|------|------| | Server | OpenLDAP | Keycloak (or any SAML IdP) | | User Location | DN/OU structure | SAML attributes | | Branch | Extracted from DN | Custom attribute in SAML | | Roles | LDAP group membership | SAML role attribute | | Password | Validated via LDAP bind | IdP handles authentication | | Groups | Extracted via group search | Included in SAML response |
Security Considerations¶
- Certificate Management
- Validate X509 certificate from IdP
- Use HTTPS in production
-
Regular certificate rotation
-
Assertion Validation
- Always verify assertion signature
- Check NotBefore/NotOnOrAfter timestamps
- Validate recipient ACS URL
-
Verify issuer matches expected IdP
-
Session Management
- Use secure session cookies
- Implement logout (SAML SLO)
-
Clear session on invalid assertions
-
Attribute Security
- Don't trust client-submitted attributes
- Validate all attributes server-side
- Use HTTPS for all SAML exchanges