SAML 2.0 Identity Provider API

SAML 2.0 IdP endpoints for enterprise integrations including configuration management, certificate handling, and SSO metadata.

Updated Nov 22, 2025
Edit on GitHub
saml identity-provider enterprise sso

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:unspecified
  • urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
  • urn: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 address
  • id: 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 token
  • 403 Forbidden: User is not an admin/owner, or organization is not active
  • 404 Not Found: Organization or service not found

Important Notes:

  • You must generate a signing certificate (see below) before SAML can be used
  • Both sign_assertions and sign_response set to true is 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 enabled
  • entity_id (string | null): Service Provider’s entity ID
  • acs_url (string | null): Assertion Consumer Service URL
  • slo_url (string | null): Single Logout URL (optional)
  • name_id_format (string | null): NameID format configuration
  • attribute_mapping (object | null): Custom attribute mappings
  • sign_assertions (boolean): Whether assertions are signed
  • sign_response (boolean): Whether responses are signed
  • has_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 format
  • valid_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 certificate
  • created_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 permissions
  • 500 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:

  1. Generate a new certificate using this endpoint
  2. Update the Service Provider with the new public certificate
  3. The old certificate is automatically deactivated
  4. 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 identifier
  • KeyDescriptor: Public signing certificate
  • NameIDFormat: Supported NameID formats
  • SingleSignOnService: SSO endpoint URLs for different SAML bindings
  • SingleLogoutService: SLO endpoint URLs for different SAML bindings
  • Organization: Organization display information

Error Responses:

  • 403 Forbidden: Organization is not active
  • 404 Not Found: Service not found
  • 400 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):

  1. Navigate to Setup > Identity > Single Sign-On Settings
  2. Click “New from Metadata File”
  3. Enter metadata URL: https://sso.example.com/saml/acme-corp/main-app/metadata
  4. 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:

  1. Service Provider Initiates SSO:

    • User clicks “Login” in third-party app (e.g., Salesforce)
    • SP generates SAMLRequest and redirects user to this endpoint
  2. 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
  3. 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
  4. User Authenticates:

    • User selects OAuth provider (GitHub/Google/Microsoft) or password login
    • Standard SSO authentication flow completes (with MFA if enabled)
  5. 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
  6. 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 XML
  • 403 Forbidden: Organization not active, SAML not enabled
  • 404 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:

  1. 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
  2. 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)
  3. Session Invalidation:

    • IdP invalidates all user sessions for the specific service
    • Clears session tokens and refresh tokens
    • Logs the logout event for audit purposes
  4. LogoutResponse Generation:

    • IdP creates a SAML LogoutResponse with Success status
    • Response includes InResponseTo referencing the original request ID
    • Response is signed according to service configuration
  5. 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 email
  • urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress - Uses user email
  • urn: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 enabled
  • 404 Not Found: Service not found
  • 500 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_url from 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:

  1. SAML is enabled for the service
  2. Active signing certificate exists
  3. slo_url is configured (optional but recommended)
  4. 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:

  1. User arrives at authentication page
  2. Sees branded login interface (if branding configured)
  3. Clicks OAuth provider button (e.g., “Continue with GitHub”)
  4. Redirected to OAuth provider for authorization
  5. Returns to IdP after OAuth success
  6. IdP generates signed SAMLResponse
  7. User’s browser submits SAMLResponse to Service Provider
  8. 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 InResponseTo attribute
  • Relay state from SP is preserved

IdP-Initiated:

  • No SAMLRequest received by IdP
  • SAMLResponse has NO InResponseTo attribute
  • No SAML state stored in database
  • User must be pre-authenticated with IdP

Error Responses:

  • 400 Bad Request: SAML not enabled, no ACS URL configured
  • 403 Forbidden: Organization not active
  • 404 Not Found: Organization or service not found
  • 500 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)

  1. Provide metadata URL to Service Provider:
    https://sso.example.com/saml/acme-corp/main-app/metadata
    
  2. Service Provider automatically imports IdP configuration

Option B: Manual Configuration

  1. Get certificate:
    curl https://sso.example.com/api/organizations/acme-corp/services/main-app/saml/certificate \
      -H "Authorization: Bearer {jwt_token}"
    
  2. 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

Step 3: Test Authentication

SP-Initiated Flow:

  1. User navigates to Service Provider
  2. Clicks “Login with SSO” or similar
  3. SP redirects to IdP SSO endpoint with SAMLRequest
  4. User authenticates via OAuth or password
  5. IdP generates signed SAMLResponse
  6. User is automatically submitted back to SP
  7. User is logged into Service Provider

IdP-Initiated Flow:

  1. User logs into your platform/dashboard
  2. Clicks “Login to Salesforce” button
  3. Application calls IdP-initiated login endpoint
  4. IdP generates unsolicited SAMLResponse
  5. User is automatically submitted to SP
  6. 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:

  1. Setup > Identity > Single Sign-On Settings
  2. Enable SAML
  3. New from Metadata File
  4. Enter: https://sso.example.com/saml/acme-corp/main-app/metadata
  5. 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:

  1. IAM > Identity Providers > Create Provider
  2. Provider Type: SAML
  3. Metadata: Upload metadata XML or use URL
  4. 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:

  1. Admin Console > Security > Authentication > SSO with third party IdP
  2. Enable SSO
  3. 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/certificate endpoint

“SAML is not enabled for this service”

  • Cause: SAML configuration not created or disabled
  • Solution: Create/enable SAML configuration using POST /saml endpoint

“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:
    1. Verify SP has correct IdP certificate
    2. Check entity_id matches SP configuration
    3. Verify acs_url is correct
    4. 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.updated with action: saml_configured
  • service.updated with action: saml_disabled
  • service.updated with action: saml_config_deleted
  • service.updated with action: saml_certificate_generated

Security Best Practices

  1. Enable Signing:

    • Set both sign_assertions: true and sign_response: true
    • Ensures integrity and authenticity of SAML assertions
  2. Certificate Rotation:

    • Rotate certificates before 3-year expiration
    • Update Service Provider with new certificate before deactivating old one
    • Monitor certificate expiration dates
  3. 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
  4. Monitor Access:

    • Review audit logs regularly for SAML configuration changes
    • Track SAML login events via analytics endpoints
    • Set up alerts for suspicious activity
  5. Test Thoroughly:

    • Test both SP-initiated and IdP-initiated flows
    • Verify attribute mapping works correctly
    • Test with MFA enabled
    • Validate session timeout behavior
  6. 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