SAML 2.0 Identity Provider API
Overview
The SAML 2.0 Identity Provider (IdP) API enables your services to provide single sign-on into third-party SAML Service Providers such as Salesforce, AWS Console, Google Workspace, and other enterprise applications. The platform acts as a SAML IdP, authenticating users via OAuth providers or password authentication, then generating signed SAML assertions for seamless SSO into integrated services.
This implementation follows the SAML 2.0 specification with XML-DSIG signature support, encrypted private key storage, configurable attribute mapping, and support for both SP-initiated and IdP-initiated flows.
Key Features
- Full SAML 2.0 IdP Implementation: Complete support for SAML authentication assertions
- XML-DSIG Signatures: RSA-SHA256 signing of assertions and responses for security
- Encrypted Key Storage: Private keys encrypted at rest with AES-GCM encryption
- Attribute Mapping: Configurable mapping of user attributes to SAML attributes
- Multiple Bindings: Support for HTTP-POST and HTTP-Redirect bindings
- SP-Initiated SSO: Standard SAML flow where Service Provider initiates authentication
- IdP-Initiated SSO: Direct login from IdP dashboard to Service Provider
- Single Logout (SLO): Support for SAML Single Logout to invalidate sessions globally
- Key Rotation: Support for certificate rotation with active key management
- MFA Integration: Seamless integration with multi-factor authentication
- Relay State Preservation: Maintains SP state throughout authentication flow
Endpoints Summary
| Method | Path | Description |
|---|---|---|
POST |
/api/organizations/:org_slug/services/:service_slug/saml |
Configure SAML IdP settings |
GET |
/api/organizations/:org_slug/services/:service_slug/saml |
Get SAML configuration |
DELETE |
/api/organizations/:org_slug/services/:service_slug/saml |
Delete SAML configuration |
POST |
/api/organizations/:org_slug/services/:service_slug/saml/certificate |
Generate signing certificate |
GET |
/api/organizations/:org_slug/services/:service_slug/saml/certificate |
Get active certificate |
GET |
/api/organizations/:org_slug/services/:service_slug/saml/login |
IdP-initiated SSO |
GET |
/saml/:org_slug/:service_slug/metadata |
SAML IdP metadata XML |
GET/POST |
/saml/:org_slug/:service_slug/sso |
SAML SSO endpoint (SP-initiated) |
GET/POST |
/saml/:org_slug/:service_slug/slo |
SAML Single Logout (SLO) endpoint |
GET |
/saml/:org_slug/:service_slug/authenticate |
SAML authentication page |
SAML Configuration Management
POST /api/organizations/:org_slug/services/:service_slug/saml
Configure SAML Identity Provider settings for a service. This endpoint sets up the SAML integration parameters required for the service to function as an IdP.
Authentication: Required (Organization Management JWT)
Permissions: Organization owner or admin
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
enabled |
boolean | Yes | Enable or disable SAML IdP functionality |
entity_id |
string | Yes (if enabled) | Service Provider’s entity ID (unique identifier) |
acs_url |
string | Yes (if enabled) | Assertion Consumer Service URL (where SAMLResponse is sent) |
slo_url |
string | No | Single Logout URL (optional) |
name_id_format |
string | No | NameID format (default: emailAddress) |
attribute_mapping |
object | No | Custom attribute mappings (source field to SAML attribute name) |
sign_assertions |
boolean | No | Sign SAML assertions (default: true) |
sign_response |
boolean | No | Sign SAML response (default: true) |
Request Headers:
Authorization: Bearer {jwt_token}
Content-Type: application/json
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"entity_id": "https://acme.my.salesforce.com",
"acs_url": "https://acme.my.salesforce.com/services/auth/saml/AssertionConsumerService",
"slo_url": "https://acme.my.salesforce.com/services/auth/saml/SingleLogoutService",
"name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"attribute_mapping": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"id": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
},
"sign_assertions": true,
"sign_response": true
}'
Response (200 OK):
{
"success": true,
"message": "SAML configuration updated successfully"
}
Common NameID Formats:
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress(default, most common)urn:oasis:names:tc:SAML:1.1:nameid-format:unspecifiedurn:oasis:names:tc:SAML:2.0:nameid-format:persistenturn:oasis:names:tc:SAML:2.0:nameid-format:transient
Attribute Mapping:
The attribute_mapping field allows you to map user fields to SAML attribute names expected by the Service Provider. Built-in source fields:
email: User’s email addressid: User’s unique identifier
Example mapping for common Service Providers:
Salesforce:
{
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
}
AWS:
{
"email": "https://aws.amazon.com/SAML/Attributes/RoleSessionName"
}
Error Responses:
400 Bad Request: Invalid configuration (missing required fields, invalid URLs){ "error": "Entity ID is required when SAML is enabled" }{ "error": "Invalid ACS URL" }401 Unauthorized: Missing or invalid JWT token403 Forbidden: User is not an admin/owner, or organization is not active404 Not Found: Organization or service not found
Important Notes:
- You must generate a signing certificate (see below) before SAML can be used
- Both
sign_assertionsandsign_responseset totrueis recommended for security - The Service Provider must be configured with the IdP’s metadata URL
- Organization must be in “active” status to configure SAML
GET /api/organizations/:org_slug/services/:service_slug/saml
Retrieve the current SAML Identity Provider configuration for a service.
Authentication: Required (Organization Management JWT)
Permissions: Organization member, admin, or owner
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Request Headers:
Authorization: Bearer {jwt_token}
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response (200 OK):
{
"enabled": true,
"entity_id": "https://acme.my.salesforce.com",
"acs_url": "https://acme.my.salesforce.com/services/auth/saml/AssertionConsumerService",
"slo_url": "https://acme.my.salesforce.com/services/auth/saml/SingleLogoutService",
"name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"attribute_mapping": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
},
"sign_assertions": true,
"sign_response": true,
"has_certificate": true
}
Response Fields:
enabled(boolean): Whether SAML IdP is enabledentity_id(string | null): Service Provider’s entity IDacs_url(string | null): Assertion Consumer Service URLslo_url(string | null): Single Logout URL (optional)name_id_format(string | null): NameID format configurationattribute_mapping(object | null): Custom attribute mappingssign_assertions(boolean): Whether assertions are signedsign_response(boolean): Whether responses are signedhas_certificate(boolean): Whether an active signing certificate exists
DELETE /api/organizations/:org_slug/services/:service_slug/saml
Delete SAML Identity Provider configuration for a service. This will disable SAML and deactivate all associated signing certificates.
Authentication: Required (Organization Management JWT)
Permissions: Organization owner or admin
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Request Headers:
Authorization: Bearer {jwt_token}
Example Request:
curl -X DELETE https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response (200 OK):
{
"success": true,
"message": "SAML configuration deleted successfully"
}
Important Notes:
- This action deactivates all signing certificates
- Users will no longer be able to authenticate via SAML to the Service Provider
- Configuration can be re-enabled by creating new settings and certificate
Certificate Management
POST /api/organizations/:org_slug/services/:service_slug/saml/certificate
Generate a new self-signed X.509 certificate and RSA key pair for signing SAML assertions. The private key is encrypted before storage.
Authentication: Required (Organization Management JWT)
Permissions: Organization owner or admin
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Request Headers:
Authorization: Bearer {jwt_token}
Example Request:
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml/certificate \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response (200 OK):
{
"public_key": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKZ...\n-----END CERTIFICATE-----",
"valid_from": "2025-01-15T10:30:00Z",
"valid_until": "2028-01-15T10:30:00Z",
"is_active": true,
"created_at": "2025-01-15T10:30:00Z"
}
Response Fields:
public_key(string): X.509 certificate in PEM formatvalid_from(string): Certificate validity start date (ISO 8601)valid_until(string): Certificate expiration date (ISO 8601, 3 years from creation)is_active(boolean): Whether this is the active signing certificatecreated_at(string): Certificate creation timestamp (ISO 8601)
Error Responses:
400 Bad Request: SAML must be enabled before generating certificate{ "error": "SAML must be enabled before generating certificate" }403 Forbidden: Organization not active, or user lacks permissions500 Internal Server Error: Encryption service not available or certificate generation failed
Important Notes:
- Certificate is valid for 3 years from creation
- Private key is encrypted using AES-GCM before database storage
- Generating a new certificate automatically deactivates any existing active certificates
- The public certificate must be provided to the Service Provider during SAML setup
- Only one certificate can be active per service at a time
Certificate Rotation:
To rotate certificates:
- Generate a new certificate using this endpoint
- Update the Service Provider with the new public certificate
- The old certificate is automatically deactivated
- Allow for a grace period before removing old certificate from SP configuration
GET /api/organizations/:org_slug/services/:service_slug/saml/certificate
Retrieve the currently active SAML signing certificate for a service.
Authentication: Required (Organization Management JWT)
Permissions: Organization member, admin, or owner
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Request Headers:
Authorization: Bearer {jwt_token}
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/services/main-app/saml/certificate \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response (200 OK):
{
"public_key": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKZ...\n-----END CERTIFICATE-----",
"valid_from": "2025-01-15T10:30:00Z",
"valid_until": "2028-01-15T10:30:00Z",
"is_active": true,
"created_at": "2025-01-15T10:30:00Z"
}
Error Responses:
404 Not Found: No active SAML certificate exists{ "error": "No active SAML certificate found" }
SAML SSO Endpoints
GET /saml/:org_slug/:service_slug/metadata
Retrieve SAML Identity Provider metadata in XML format. This endpoint is public and provides all the information a Service Provider needs to integrate with the IdP.
Authentication: None required (public endpoint)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Example Request:
curl https://sso.example.com/saml/acme-corp/main-app/metadata
Response (200 OK):
Content-Type: application/samlmetadata+xml
Response Body (XML):
<?xml version="1.0" encoding="UTF-8"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://sso.example.com/saml/acme-corp/main-app">
<IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>MIIDXTCCAkWgAwIBAgIJAKZ...</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.example.com/saml/acme-corp/main-app/sso"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.example.com/saml/acme-corp/main-app/sso"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.example.com/saml/acme-corp/main-app/slo"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.example.com/saml/acme-corp/main-app/slo"/>
</IDPSSODescriptor>
<Organization>
<OrganizationName xml:lang="en">Acme Corporation</OrganizationName>
<OrganizationDisplayName xml:lang="en">Acme Corporation</OrganizationDisplayName>
<OrganizationURL xml:lang="en">https://sso.example.com</OrganizationURL>
</Organization>
</EntityDescriptor>
Metadata Fields:
EntityDescriptor@entityID: IdP’s unique entity identifierKeyDescriptor: Public signing certificateNameIDFormat: Supported NameID formatsSingleSignOnService: SSO endpoint URLs for different SAML bindingsSingleLogoutService: SLO endpoint URLs for different SAML bindingsOrganization: Organization display information
Error Responses:
403 Forbidden: Organization is not active404 Not Found: Service not found400 Bad Request: SAML is not enabled for this service
Usage:
Many Service Providers can automatically import IdP metadata from this URL. Provide this URL during SAML configuration in the Service Provider’s admin panel.
Example (Salesforce):
- Navigate to Setup > Identity > Single Sign-On Settings
- Click “New from Metadata File”
- Enter metadata URL:
https://sso.example.com/saml/acme-corp/main-app/metadata - Salesforce automatically imports IdP configuration
GET/POST /saml/:org_slug/:service_slug/sso
SAML Single Sign-On endpoint. Receives a SAMLRequest from the Service Provider and initiates the authentication flow. Supports both HTTP-Redirect (GET) and HTTP-POST bindings.
Authentication: None required (public endpoint)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Query/Form Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
SAMLRequest |
string | Yes | Base64-encoded (optionally deflated) SAML AuthnRequest |
RelayState |
string | No | Opaque value to be returned to SP after authentication |
Example Request (HTTP-Redirect binding):
# This request is typically initiated by the Service Provider
# User's browser is redirected to:
https://sso.example.com/saml/acme-corp/main-app/sso?SAMLRequest=nZFBa4MwGIb%2FSsh...&RelayState=https://sp.example.com/resource
Response: 302 Redirect to authentication page
Authentication Flow:
-
Service Provider Initiates SSO:
- User clicks “Login” in third-party app (e.g., Salesforce)
- SP generates SAMLRequest and redirects user to this endpoint
-
IdP Processes SAMLRequest:
- Decodes and parses SAMLRequest XML
- Extracts request ID, issuer (SP entity ID), and ACS URL
- Validates destination matches expected SSO endpoint
- Creates SAML state record to track the authentication session
-
User is Redirected to Authentication Page:
- User is redirected to
/saml/:org_slug/:service_slug/authenticate?state={state_id} - Authentication page displays available login options
- User is redirected to
-
User Authenticates:
- User selects OAuth provider (GitHub/Google/Microsoft) or password login
- Standard SSO authentication flow completes (with MFA if enabled)
-
SAMLResponse Generated:
- After successful authentication, IdP generates signed SAMLResponse
- Response includes user attributes and InResponseTo reference
- User’s browser auto-submits SAMLResponse to SP’s ACS URL
-
Service Provider Validates:
- SP validates signature using IdP’s public certificate
- SP verifies InResponseTo matches original request ID
- User is logged into Service Provider
Error Responses:
400 Bad Request: Invalid or missing SAMLRequest, malformed XML403 Forbidden: Organization not active, SAML not enabled404 Not Found: Service not found
Security Features:
- SAMLRequest validation and parsing
- SAML state expires after 15 minutes
- InResponseTo correlation prevents replay attacks
- Signature verification at Service Provider
- RelayState preservation maintains SP session context
GET/POST /saml/:org_slug/:service_slug/slo
SAML Single Logout (SLO) endpoint. Receives a LogoutRequest from the Service Provider, invalidates the user’s sessions, and returns a signed LogoutResponse. This endpoint enables global session termination across federated services.
Authentication: None required (public endpoint)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Query/Form Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
SAMLRequest |
string | Yes | Base64-encoded (optionally deflated) SAML LogoutRequest |
RelayState |
string | No | Opaque value to be returned to SP after logout |
Example Request (HTTP-Redirect binding):
# This request is typically initiated by the Service Provider when user logs out
# User's browser is redirected to:
https://sso.example.com/saml/acme-corp/main-app/slo?SAMLRequest=nZFBa4MwGIb%2FSsh...&RelayState=https://sp.example.com/logout
Example Request (HTTP-POST binding):
<form method="post" action="https://sso.example.com/saml/acme-corp/main-app/slo">
<input type="hidden" name="SAMLRequest" value="PHNhbWxwOkxvZ291dFJlcXVlc3Q..." />
<input type="hidden" name="RelayState" value="https://sp.example.com/logout" />
</form>
Response: HTML page with auto-submitting form containing LogoutResponse
Response Body (HTML):
<!DOCTYPE html>
<html>
<head>
<title>Logging out...</title>
<style>
body { font-family: sans-serif; background: #f5f5f5; padding: 20px; text-align: center; }
.container { max-width: 400px; margin: 50px auto; background: white; padding: 40px; border-radius: 8px; }
.spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%;
width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 20px auto; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>
</head>
<body onload="document.forms[0].submit()">
<div class="container">
<div class="spinner"></div>
<h2>Logging out...</h2>
<p>Please wait while we complete the logout process.</p>
</div>
<form method="post" action="https://sp.example.com/slo" style="display: none;">
<input type="hidden" name="SAMLResponse" value="PHNhbWxwOkxvZ291dFJlc3BvbnNl..." />
<input type="hidden" name="RelayState" value="https://sp.example.com/logout" />
<noscript>
<p>JavaScript is disabled. Please click the button below to continue.</p>
<input type="submit" value="Continue" />
</noscript>
</form>
</body>
</html>
Single Logout Flow:
-
Service Provider Initiates Logout:
- User clicks “Logout” in the Service Provider (e.g., Salesforce)
- SP generates a SAML LogoutRequest containing the user’s NameID
- User’s browser is redirected to IdP’s SLO endpoint
-
IdP Processes LogoutRequest:
- Decodes and parses LogoutRequest XML
- Extracts request ID, issuer, and NameID (user identifier)
- Validates destination matches expected SLO endpoint
- Identifies user from NameID (typically email address)
-
Session Invalidation:
- IdP invalidates all user sessions for the specific service
- Clears session tokens and refresh tokens
- Logs the logout event for audit purposes
-
LogoutResponse Generation:
- IdP creates a SAML LogoutResponse with Success status
- Response includes
InResponseToreferencing the original request ID - Response is signed according to service configuration
-
Return to Service Provider:
- User’s browser auto-submits LogoutResponse to SP’s SLO URL
- SP validates the response signature
- SP confirms logout is complete
- User is fully logged out from both systems
SAML LogoutRequest Structure:
<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_request_123"
Version="2.0"
IssueInstant="2025-01-15T10:30:00Z"
Destination="https://sso.example.com/saml/acme-corp/main-app/slo">
<saml:Issuer>https://acme.my.salesforce.com</saml:Issuer>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">user@example.com</saml:NameID>
</samlp:LogoutRequest>
SAML LogoutResponse Structure (Generated by IdP):
<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_response_456"
Version="2.0"
IssueInstant="2025-01-15T10:30:05Z"
Destination="https://acme.my.salesforce.com/slo"
InResponseTo="_request_123">
<saml:Issuer>https://sso.example.com/saml/acme-corp/main-app</saml:Issuer>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<!-- XML signature -->
</Signature>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
</samlp:LogoutResponse>
NameID Format Support:
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress(default) - Uses user emailurn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress- Uses user emailurn:oasis:names:tc:SAML:2.0:nameid-format:persistent- Uses user ID or email (context-dependent)urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified- Attempts email or user ID match
Error Responses:
400 Bad Request: Invalid or missing SAMLRequest, malformed XML, missing NameID{ "error": "SAMLRequest parameter is required" }{ "error": "NameID is required" }{ "error": "No SLO URL configured and no issuer in request" }403 Forbidden: Organization not active, SAML not enabled404 Not Found: Service not found500 Internal Server Error: No active signing key, encryption service unavailable
Important Considerations:
Session Scope:
- Only invalidates sessions for the specific service (not all services)
- User remains logged in to other Service Providers
- User remains logged in to the IdP platform itself
User Not Found:
- If NameID doesn’t match any user, SLO still returns Success status
- This prevents information disclosure about valid users
- Logged as warning for administrative review
SLO URL Resolution:
- Uses configured
slo_urlfrom service SAML configuration - Falls back to issuer URL from LogoutRequest if SLO URL not configured
- Ensure SLO URL is properly configured for reliable logout flow
Security Features:
- Request validation and signature verification (if signed by SP)
- Response signing with IdP’s private key
- InResponseTo correlation for request-response matching
- RelayState preservation for SP state management
- Destination validation to prevent request forgery
- Support for both HTTP-POST and HTTP-Redirect bindings
Configuration Requirements:
To enable SLO, ensure:
- SAML is enabled for the service
- Active signing certificate exists
slo_urlis configured (optional but recommended)- Service Provider is configured with IdP’s SLO endpoint from metadata
Example Configuration:
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"entity_id": "https://acme.my.salesforce.com",
"acs_url": "https://acme.my.salesforce.com/services/auth/saml/AssertionConsumerService",
"slo_url": "https://acme.my.salesforce.com/services/auth/saml/SingleLogoutService",
"name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"sign_assertions": true,
"sign_response": true
}'
Enterprise Compliance:
- Addresses security requirements for global session termination
- Supports compliance frameworks (SOC 2, ISO 27001, GDPR)
- Enables secure offboarding workflows
- Provides audit trail for session invalidation
GET /saml/:org_slug/:service_slug/authenticate
SAML authentication page that displays login options to the user during SP-initiated SSO flow. This page is automatically shown after SAMLRequest validation.
Authentication: None required (public endpoint)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
state |
string | Yes | SAML state ID from SSO initiation |
Example Request:
# User is automatically redirected to this page
https://sso.example.com/saml/acme-corp/main-app/authenticate?state=550e8400-e29b-41d4-a716-446655440000
Response: HTML page with login options
Page Features:
- Displays organization branding (logo and colors if configured)
- Shows available OAuth provider buttons (GitHub, Google, Microsoft)
- Password login form (if enabled)
- “Sign In to Continue” messaging
- Links include SAML state parameter for flow continuation
Error Responses:
400 Bad Request: Invalid or expired SAML state
User Experience:
- User arrives at authentication page
- Sees branded login interface (if branding configured)
- Clicks OAuth provider button (e.g., “Continue with GitHub”)
- Redirected to OAuth provider for authorization
- Returns to IdP after OAuth success
- IdP generates signed SAMLResponse
- User’s browser submits SAMLResponse to Service Provider
- User is logged into Service Provider
GET /api/organizations/:org_slug/services/:service_slug/saml/login
IdP-Initiated SAML SSO. Allows an authenticated user to directly initiate SSO to a Service Provider without a SAMLRequest (unsolicited SAML response).
Authentication: Required (Organization Management JWT)
Permissions: Authenticated user with valid JWT token
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
service_slug |
string | Service slug |
Request Headers:
Authorization: Bearer {jwt_token}
Important Implementation Note: This endpoint requires JWT authentication and cannot be accessed via a simple HTML link. The frontend must make an authenticated XHR/Fetch request to retrieve the HTML form, then render/execute it in the browser.
Frontend Implementation Example:
async function initiateIdpLogin(orgSlug, serviceSlug, token) {
try {
const response = await fetch(
`/api/organizations/${orgSlug}/services/${serviceSlug}/saml/login`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'text/html'
}
}
);
if (response.ok) {
const htmlContent = await response.text();
// Render the HTML content in a new window or iframe
const popup = window.open('', '_blank');
popup.document.write(htmlContent);
}
} catch (error) {
console.error('Failed to initiate IdP login:', error);
}
}
Direct API Request (for testing):
curl -X GET https://sso.example.com/api/organizations/acme-corp/services/main-app/saml/login \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Response: HTML page with auto-submitting form
Response Body (HTML):
<!DOCTYPE html>
<html>
<head>
<title>Signing you in...</title>
<style>
body { font-family: sans-serif; background: #f5f5f5; padding: 20px; text-align: center; }
.container { max-width: 400px; margin: 50px auto; background: white; padding: 40px; border-radius: 8px; }
.spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%;
width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 20px auto; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>
</head>
<body onload="document.forms[0].submit()">
<div class="container">
<div class="spinner"></div>
<h2>Signing you in...</h2>
<p>Please wait while we redirect you to the service.</p>
</div>
<form method="post" action="https://sp.example.com/acs" style="display: none;">
<input type="hidden" name="SAMLResponse" value="PHNhbWxwOlJlc3Bv..." />
<noscript>
<p>JavaScript is disabled. Please click the button below to continue.</p>
<input type="submit" value="Continue" />
</noscript>
</form>
</body>
</html>
Flow Differences from SP-Initiated:
SP-Initiated (standard):
- Service Provider sends SAMLRequest to IdP
- SAMLResponse includes
InResponseToattribute - Relay state from SP is preserved
IdP-Initiated:
- No SAMLRequest received by IdP
- SAMLResponse has NO
InResponseToattribute - No SAML state stored in database
- User must be pre-authenticated with IdP
Error Responses:
400 Bad Request: SAML not enabled, no ACS URL configured403 Forbidden: Organization not active404 Not Found: Organization or service not found500 Internal Server Error: No active signing key, encryption service unavailable
Use Cases:
- Dashboard portals where users click to access integrated services
- Quick access links to frequently used Service Providers
- Admin panels with “Login to Service” buttons
- Mobile apps with direct SSO capabilities
Security Notes:
- Requires active organization status
- Requires SAML enabled and configured
- Requires active signing certificate
- User must have valid authenticated session
- SAMLResponse signed according to service configuration
- Some Service Providers may not accept unsolicited responses (check SP documentation)
Complete SAML Integration Workflow
Step 1: Configure SAML IdP
# 1. Create SAML configuration
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"entity_id": "https://acme.my.salesforce.com",
"acs_url": "https://acme.my.salesforce.com/services/auth/saml/AssertionConsumerService",
"name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"sign_assertions": true,
"sign_response": true
}'
# 2. Generate signing certificate
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml/certificate \
-H "Authorization: Bearer {jwt_token}"
Step 2: Configure Service Provider
Option A: Metadata URL (Recommended)
- Provide metadata URL to Service Provider:
https://sso.example.com/saml/acme-corp/main-app/metadata - Service Provider automatically imports IdP configuration
Option B: Manual Configuration
- Get certificate:
curl https://sso.example.com/api/organizations/acme-corp/services/main-app/saml/certificate \ -H "Authorization: Bearer {jwt_token}" - Configure SP with:
- IdP Entity ID:
https://sso.example.com/saml/acme-corp/main-app - SSO URL:
https://sso.example.com/saml/acme-corp/main-app/sso - Signing Certificate: (from response)
- NameID Format:
emailAddress
- IdP Entity ID:
Step 3: Test Authentication
SP-Initiated Flow:
- User navigates to Service Provider
- Clicks “Login with SSO” or similar
- SP redirects to IdP SSO endpoint with SAMLRequest
- User authenticates via OAuth or password
- IdP generates signed SAMLResponse
- User is automatically submitted back to SP
- User is logged into Service Provider
IdP-Initiated Flow:
- User logs into your platform/dashboard
- Clicks “Login to Salesforce” button
- Application calls IdP-initiated login endpoint
- IdP generates unsolicited SAMLResponse
- User is automatically submitted to SP
- User is logged into Service Provider
Step 4: Monitor and Maintain
# View SAML configuration
curl https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer {jwt_token}"
# Check audit logs for SAML events
curl https://sso.example.com/api/organizations/acme-corp/audit-log?action=service.updated \
-H "Authorization: Bearer {jwt_token}"
# Rotate certificate (before expiration)
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml/certificate \
-H "Authorization: Bearer {jwt_token}"
Service Provider Integration Examples
Salesforce Integration
Configuration:
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"entity_id": "https://acme.my.salesforce.com",
"acs_url": "https://acme.my.salesforce.com/services/auth/saml/AssertionConsumerService",
"name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"attribute_mapping": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
},
"sign_assertions": true,
"sign_response": true
}'
Salesforce Setup:
- Setup > Identity > Single Sign-On Settings
- Enable SAML
- New from Metadata File
- Enter:
https://sso.example.com/saml/acme-corp/main-app/metadata - Save
AWS IAM Integration
Configuration:
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"entity_id": "urn:amazon:webservices",
"acs_url": "https://signin.aws.amazon.com/saml",
"name_id_format": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
"attribute_mapping": {
"email": "https://aws.amazon.com/SAML/Attributes/RoleSessionName"
},
"sign_assertions": true,
"sign_response": true
}'
AWS Setup:
- IAM > Identity Providers > Create Provider
- Provider Type: SAML
- Metadata: Upload metadata XML or use URL
- Create IAM roles and trust policies
Google Workspace Integration
Configuration:
curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/saml \
-H "Authorization: Bearer {jwt_token}" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"entity_id": "google.com",
"acs_url": "https://www.google.com/a/acme.com/acs",
"name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"sign_assertions": true,
"sign_response": false
}'
Google Workspace Setup:
- Admin Console > Security > Authentication > SSO with third party IdP
- Enable SSO
- Upload IdP metadata or enter details manually
Troubleshooting
Common Issues
“No active SAML certificate found”
- Cause: Certificate not generated or expired
- Solution: Generate new certificate using POST
/saml/certificateendpoint
“SAML is not enabled for this service”
- Cause: SAML configuration not created or disabled
- Solution: Create/enable SAML configuration using POST
/samlendpoint
“Invalid or expired SAML state”
- Cause: SAML state expired (15 minute timeout) or already used
- Solution: User should restart login flow from Service Provider
Service Provider rejects SAMLResponse
- Cause: Signature validation failure, certificate mismatch, or configuration mismatch
- Solution:
- Verify SP has correct IdP certificate
- Check entity_id matches SP configuration
- Verify acs_url is correct
- Ensure both sign_assertions and sign_response are enabled
IdP-Initiated flow not working
- Cause: Some Service Providers don’t accept unsolicited responses
- Solution: Check SP documentation, use SP-initiated flow instead
Audit Trail
All SAML configuration changes are logged in the organization audit log:
# View SAML-related audit events
curl "https://sso.example.com/api/organizations/acme-corp/audit-log?action=service.updated" \
-H "Authorization: Bearer {jwt_token}"
SAML Audit Events:
service.updatedwith action:saml_configuredservice.updatedwith action:saml_disabledservice.updatedwith action:saml_config_deletedservice.updatedwith action:saml_certificate_generated
Security Best Practices
-
Enable Signing:
- Set both
sign_assertions: trueandsign_response: true - Ensures integrity and authenticity of SAML assertions
- Set both
-
Certificate Rotation:
- Rotate certificates before 3-year expiration
- Update Service Provider with new certificate before deactivating old one
- Monitor certificate expiration dates
-
Secure Configuration:
- Validate entity_id matches actual Service Provider
- Verify acs_url is HTTPS and belongs to legitimate Service Provider
- Use proper NameID format for your use case
-
Monitor Access:
- Review audit logs regularly for SAML configuration changes
- Track SAML login events via analytics endpoints
- Set up alerts for suspicious activity
-
Test Thoroughly:
- Test both SP-initiated and IdP-initiated flows
- Verify attribute mapping works correctly
- Test with MFA enabled
- Validate session timeout behavior
-
Keep Metadata Updated:
- Provide Service Providers with metadata URL for automatic updates
- Notify Service Provider administrators when rotating certificates
- Document integration procedures for your team