SAML Metadata and SSO

Publish SAML metadata and implement SP-initiated or IdP-initiated sign-on

AuthOS release 0.8.2 API v1 Latest-only documentation
Updated Jul 15, 2026 Edit this page
On this page

SAML SSO Endpoints

GET /saml/:org_slug/:service_slug/metadata

Retrieve the implemented SAML Identity Provider metadata in XML format. Service Provider requirements vary; validate that the returned metadata contains the fields required by the exact product and version being integrated.

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 contributes to replay defense; public SAML abuse-case evidence is still incomplete
  • Signature verification at Service Provider
  • RelayState preservation maintains SP session context

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: A current organization member or an authenticated identity scoped to the exact organization and service. Platform ownership alone does not grant assertion access.

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)