SAML Flow
Overview
Two separate SAML integrations exist:
Integration |
Purpose |
Endpoints |
Use Case |
|---|---|---|---|
Site SAML |
Pub lic/internal site access |
|
Employee intranet, gated content |
CMS SAML |
Admin CMS access |
|
Content editors, a dministrators |
Site SAML Flow
Provides SSO for internal/external site separation. Employees access internal content; public users see external content.
Architecture
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ External │ │ Internal │ │ IdP │
│ Site │ │ Site │ │ (Entra) │
│ logout-dest │ │ login-dest │ │ │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ /saml/auth │ │
│◄─────────────────►│ │
│ (routing check) │ │
│ │ │
│ │ /saml/login │
│ │──────────────────►│
│ │ AuthnRequest │
│ │ │
│ │ /saml/response │
│ │◄──────────────────│
│ │ SAMLResponse │
│ │ │
│ /saml/logout │ │
│◄──────────────────│──────────────────►│
│ │ LogoutRequest │
│ │ │
Login Flow
1. User visits internal site (login-destination domain)
│
▼
2. /saml/auth checks auth cookie
│
├─► Has cookie + valid assertion → Allow access
│
└─► No cookie → Redirect to /saml/login
│
▼
3. /saml/login generates AuthnRequest
- Creates XML with unique ID, timestamp
- Encodes: XML → Deflate → Base64 → URL-encode
- Redirects to IdP endpoint
│
▼
4. User authenticates at IdP
│
▼
5. IdP POSTs to /saml/response
- SAMLResponse (encrypted assertion)
- RelayState (original URL)
│
▼
6. /saml/response validates
- Decrypts assertion (RSA-OAEP + AES-256-CBC)
- Validates issuer, timestamps
- Checks replay (assertion ID uniqueness)
│
▼
7. Creates/updates user, sets auth cookie
│
▼
8. Redirects to RelayState or login-destination
Logout Flow
1. User clicks logout on internal site
│
▼
2. /saml/logout
- Requires authenticated user
- Creates LogoutRequest XML
- Signs with RSA-SHA256
- Deletes auth cookie
│
▼
3. Redirects to IdP with signed LogoutRequest
│
▼
4. IdP terminates session
│
▼
5. User lands on logout-destination (external site)
Domain Routing (/saml/auth)
The /saml/auth endpoint enforces access control based on domain:
Current Domain |
Auth Status |
Action |
|---|---|---|
login-destination (internal) |
Not authenticated |
→ Redirect to IdP |
login-destination (internal) |
Authenticated |
→ Allow |
logout-destination (external) |
Authenticated |
→ Redirect to internal |
logout-destination (external) |
Not authenticated |
→ Allow |
CMS SAML Flow
Provides SSO for CMS admin access with role-based authorization.
Architecture
┌─────────────┐ ┌─────────────┐
│ CMS │ │ IdP │
│ /cms/... │ │ (Azure AD) │
└──────┬──────┘ └──────┬──────┘
│ │
│ GET /saml/cms/login │
│──────────────────────────────────────►│
│ AuthnRequest │
│ │
│ POST /saml/cms/login │
│◄──────────────────────────────────────│
│ SAMLResponse │
│ │
│ POST /saml/cms/logout │
│◄──────────────────────────────────────│
│ LogoutResponse │
│ │
Login Flow
1. User visits CMS without session
│
▼
2. Redirected to GET /saml/cms/login
- Creates AuthnRequest with SP issuer
- Redirects to IdP
│
▼
3. User authenticates at IdP
│
▼
4. IdP POSTs to /saml/cms/login
- SAMLResponse with user attributes
│
▼
5. /saml/cms/login validates
- Decrypts assertion
- Validates issuer, timestamps
- Checks replay attack
│
▼
6. Extracts attributes using configured identifiers
- first-name-identifier → firstName
- last-name-identifier → lastName
- user-identifier → email
- role-identifier → roles
│
▼
7. Role validation
- Extracts user's role from assertion
- Checks if role exists in configured `roles` list
│
├─► Role matches → Continue
│
└─► No match → 403 Forbidden
│
▼
8. User provisioning
- Creates or updates user with attributes
- Assigns to CMS user group
- Sets country to Netherlands
│
▼
9. Sets session (_ID key), redirects to CMS
Key Differences from Site SAML
Aspect |
Site SAML |
CMS SAML |
|---|---|---|
Authorization* |
Any authenticated user |
Role must match
|
Attributes |
Project-hardcoded mapping |
Configurable identifiers |
User groups |
Internal website groups |
CMS user group |
Certificates |
|
|
Session |
Auth cookie |
|
Logout |
SP-initiated, signed |
IdP-initiated only |