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 attempts to invalidate the user’s 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
- The Service Provider handles the response; operators must verify resulting session state in 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
}'
Governance considerations:
- SLO can contribute to a global-session-termination workflow
- Session invalidation events can contribute to an operator’s audit evidence
- Operators must validate IdP interoperability, failure handling, offboarding completeness, and applicable control requirements
- This capability does not make a deployment SOC 2, ISO 27001, or GDPR compliant