Enterprise SSO (SAML)
This document defines the draft SAML 2.0 single sign-on configuration for the IDP. It focuses on per-tenant configuration, metadata ingestion, and certificate rotation.
Status: Draft (groundwork for Phase 3 of the IDP roadmap)
Goals
- Support per-tenant SAML configuration with clear validation rules.
- Allow metadata ingestion via URL fetch or manual upload.
- Provide a safe certificate rotation strategy without downtime.
Non-Goals
- SCIM provisioning (covered in the SCIM roadmap item).
- Full implementation details for UI and API (tracked in the SAML epic workstream).
Per-Tenant Configuration Schema (Draft)
This schema defines the inputs required to enable SAML for a tenant. The canonical data model is intentionally explicit and versioned to avoid metadata drift.
{
"version": "v1",
"tenantId": "<uuid>",
"enabled": true,
"mode": "enforced",
"entityId": "https://idp.digiwedge.com/saml/metadata/<tenantSlug>",
"acsUrl": "https://idp.digiwedge.com/api/auth/saml/acs/<tenantSlug>",
"sloUrl": "https://idp.digiwedge.com/api/auth/saml/slo/<tenantSlug>",
"idpMetadata": {
"source": "url",
"value": "https://idp.example.com/metadata",
"fetchedAt": "2026-01-27T12:00:00.000Z"
},
"idp": {
"entityId": "https://idp.example.com",
"ssoUrl": "https://idp.example.com/sso",
"sloUrl": "https://idp.example.com/slo",
"nameIdFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"signingCertificates": [
{
"kid": "primary",
"certificatePem": "-----BEGIN CERTIFICATE-----...",
"active": true
},
{
"kid": "next",
"certificatePem": "-----BEGIN CERTIFICATE-----...",
"active": true
}
]
},
"policy": {
"allowIdpInitiated": true,
"requireSignedAssertions": true,
"requireSignedResponse": true,
"forceReauthOnAcrChange": true
},
"mappings": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"firstName": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
"lastName": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
}
}
Field Notes
mode:disabled | optional | enforcedentityId/acsUrl/sloUrlare derived from the tenant slug and environment.idpMetadata.sourcesupportsurlorxml(manual upload).signingCertificatessupports two active certs to allow rotation.mappingsshould support attribute aliases per tenant.
Metadata Ingestion
The IDP supports two ingestion modes (see ADR-0002):
-
URL Fetch (preferred)
- Store the metadata URL and fetch on save.
- Re-fetch on a scheduled interval (e.g., every 6 hours).
- Parse and persist a normalized representation (entity ID, SSO URL, certs).
-
Manual Upload
- Store the raw XML blob for traceability.
- Parse and normalize into the same canonical fields.
Validation requirements:
- Metadata must include a valid
EntityDescriptorwith at least oneIDPSSODescriptor. - At least one signing certificate is required.
- Supported bindings: HTTP-Redirect and HTTP-POST.
Certificate Rotation
See ADR-0004 for the rotation policy. Key points:
- Allow multiple signing certs simultaneously.
- Prefer the cert marked
activebut accept any valid cert during validation. - Rotate by publishing new metadata, then remove the old cert only after a grace period.
API Contract
DTOs are defined in apps/idp/src/app/dto/saml/.
Configuration Endpoints
| Method | Path | Request DTO | Response DTO | Description |
|---|---|---|---|---|
GET | /api/auth/saml/config/:tenantId | — | SamlConfigResponseDto | Get tenant SAML config |
POST | /api/auth/saml/config/:tenantId | CreateSamlConfigDto | SamlConfigResponseDto | Create/replace config |
PATCH | /api/auth/saml/config/:tenantId | UpdateSamlConfigDto | SamlConfigResponseDto | Partial update |
DELETE | /api/auth/saml/config/:tenantId | — | { deleted: true } | Disable/remove config |
Metadata Endpoints
| Method | Path | Request DTO | Response DTO | Description |
|---|---|---|---|---|
GET | /api/auth/saml/metadata/:tenantSlug | — | XML | SP metadata for IdP configuration |
POST | /api/auth/saml/config/:tenantId/ingest-url | IngestMetadataUrlDto | MetadataIngestionResultDto | Ingest from URL |
POST | /api/auth/saml/config/:tenantId/ingest-xml | IngestMetadataXmlDto | MetadataIngestionResultDto | Ingest from XML |
POST | /api/auth/saml/config/:tenantId/refresh | RefreshMetadataDto | RefreshMetadataResultDto | Refresh cached metadata |
Authentication Endpoints
| Method | Path | Request DTO | Response DTO | Description |
|---|---|---|---|---|
GET | /api/auth/saml/login/:tenantSlug | InitiateSamlLoginDto (query) | Redirect | Initiate SP login |
POST | /api/auth/saml/acs/:tenantSlug | SamlAcsCallbackDto | SamlAuthResultDto | ACS callback |
GET | /api/auth/saml/logout/:tenantSlug | InitiateSamlLogoutDto (query) | Redirect | Initiate SP logout |
POST | /api/auth/saml/slo/:tenantSlug | SamlSloCallbackDto | Redirect | SLO callback |
Error Codes
| Code | HTTP | Description |
|---|---|---|
SAML_CONFIG_NOT_FOUND | 404 | No SAML config for tenant |
SAML_DISABLED | 403 | SAML is disabled for tenant |
METADATA_FETCH_FAILED | 422 | Could not fetch metadata URL |
METADATA_PARSE_ERROR | 422 | Invalid metadata XML |
ASSERTION_INVALID | 401 | SAML assertion validation failed |
CERTIFICATE_EXPIRED | 401 | All IdP certificates expired |
Open Questions
- Where should SAML configs be stored: Access Control DB or IDP DB?
- How do we expose tenant SSO settings in the admin UI without leaking secrets?
- Should we support signed AuthnRequests by default?