Configuring SAML Applications for Enterprise SSO in Logto: The Complete Technical Guide

Logto implements SAML-based enterprise SSO through a dedicated application type that stores configuration across three linked database records—application metadata, SAML-specific settings, and encrypted X.509 secrets—while exposing REST endpoints for SP metadata exchange and assertion handling.

Configuring SAML applications for enterprise SSO in Logto requires navigating a multi-layered architecture that separates generic application data from protocol-specific configuration and cryptographic secrets. The logto-io/logto repository provides a complete implementation spanning schema validation guards, core libraries, and REST API endpoints to handle both SP-initiated and IdP-initiated authentication flows.

Understanding the SAML Application Data Model

Logto treats SAML integrations as a distinct application type consisting of three interconnected storage layers:

  1. Application entry – Stores common fields including name, description, and customData (defined in the Applications schema)
  2. SAML configuration – Contains entityId, acsUrl, attributeMapping, encryption settings, and nameIdFormat (managed in SamlApplicationConfigs)
  3. SAML secrets – Houses X.509 certificates and private keys, exposing only the public SHA-256 fingerprint while keeping private keys encrypted (see SamlApplicationSecrets)

These records are wired together through validation guards defined in packages/schemas/src/types/saml-application.ts. The samlApplicationCreateGuard enforces that attributeMapping and spMetadata cannot be omitted separately, ensuring data integrity during creation:

// packages/schemas/src/types/saml-application.ts
export const samlApplicationCreateGuard = applicationCreateGuard
  .pick({ name: true, description: true, customData: true })
  .merge(samlAppConfigGuard.partial())
  .extend({ nameIdFormat: nameIdFormatGuard.optional().default(NameIdFormat.Persistent) });

The SamlApplicationResponse type merges generic Applications fields with SAML-specific configuration, producing the unified API response shape returned by Logto's endpoints.

Core Architecture and Source Files

The SAML implementation spans four architectural layers:

Component Responsibility Key Source File
Schema definitions Zod guards for validation and response shapes packages/schemas/src/types/saml-application.ts
Core library High-level CRUD operations and session management packages/core/src/libraries/saml-application/saml-applications.ts
REST routes API endpoints for metadata and assertions packages/core/src/routes/saml-application/index.ts
Utilities Callback URL generation for IdP-initiated flows packages/core/src/saml-application/SamlApplication/utils.ts

The core library is instantiated in packages/core/src/tenants/Libraries.ts and injected into request contexts, providing a clean separation between data access and HTTP transport concerns.

API Endpoints for SAML Lifecycle Management

The router in packages/core/src/routes/saml-application/index.ts exposes the complete SAML application lifecycle through the following endpoints:

Method Path Purpose
POST /saml-applications Create a new SAML application
GET /saml-applications/:id Retrieve application details including SAML config
GET /saml-applications/:id/metadata Return SP metadata XML for IdP consumption
POST /saml-applications/:id/callback Assertion consumer endpoint for SP and IdP flows
PATCH /saml-applications/:id Update existing SAML configuration
DELETE /saml-applications/:id Remove the SAML application

Creating a SAML Application

To provision a new SAML application, send a POST request to the /saml-applications endpoint with the required configuration:

curl -X POST https://<tenant>.logto.io/api/saml-applications \
  -H "Authorization: Bearer <admin-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My SAML App",
    "description": "Enterprise SSO integration",
    "entityId": "https://my-company.com/saml-sp",
    "acsUrl": "https://my-company.com/saml-acs",
    "attributeMapping": {
      "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
      "name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
    },
    "encryption": {
      "enabled": false
    },
    "nameIdFormat": "persistent"
  }'

The response conforms to the SamlApplicationResponse type, returning the merged application data and SAML configuration.

Retrieving SP Metadata

After creation, retrieve the Service Provider metadata XML that your Identity Provider requires:

curl -X GET https://<tenant>.logto.io/api/saml-applications/<app-id>/metadata \
  -H "Authorization: Bearer <admin-access-token>"

This returns an XML document similar to:

<EntityDescriptor entityID="https://my-company.com/saml-sp" ...>
  <SPSSODescriptor WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                               Location="https://my-company.com/saml-acs" index="1"/>
    ...
  </SPSSODescriptor>
</EntityDescriptor>

Import this XML into your IdP's SAML SP metadata configuration to establish trust.

Handling IdP-Initiated SSO

For IdP-initiated flows, the Identity Provider POSTs SAML assertions to Logto's callback URL. This URL is generated by the getSamlAppCallbackUrl function in packages/core/src/saml-application/SamlApplication/utils.ts and follows the pattern:


https://<tenant>.logto.io/api/saml-applications/<app-id>/callback

When handling assertions programmatically, forward the SAML response to this endpoint:

import fetch from 'node-fetch';

await fetch(`https://<tenant>.logto.io/api/saml-applications/${appId}/callback`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    SAMLResponse: '<base64-encoded-assertion>',
    RelayState: 'optional-state'
  })
});

Logto validates the assertion, decrypts the content if encryption is enabled, extracts the NameID according to the configured nameIdFormat, and establishes an authenticated session.

Certificate Rotation and Secret Management

SAML signing certificates are managed through the SamlApplicationSecrets table. To rotate certificates without service interruption:

curl -X POST https://<tenant>.logto.io/api/saml-applications/<app-id>/secrets \
  -H "Authorization: Bearer <admin-access-token>" \
  -H "Content-Type: application/json" \
  -d '{"lifeSpanInYears": 5}'

The API returns the new public fingerprint (SHA-256 hash) but never exposes the private key, maintaining security boundaries consistent with the implementation in packages/core/src/libraries/saml-application/saml-applications.ts.

Summary

Configuring SAML applications for enterprise SSO in Logto follows a structured three-phase approach:

  • Data modeling relies on three linked records (application, config, secrets) validated through Zod guards in packages/schemas/src/types/saml-application.ts
  • Metadata exchange uses the /saml-applications/:id/metadata endpoint to generate SP metadata for IdP consumption
  • Assertion handling occurs through the /saml-applications/:id/callback endpoint, supporting both SP-initiated and IdP-initiated flows with automatic decryption and session creation

The architecture ensures that sensitive cryptographic material remains encrypted while exposing only necessary public certificates and metadata endpoints.

Frequently Asked Questions

How does Logto store SAML application credentials securely?

Logto stores X.509 certificates and private keys in the SamlApplicationSecrets table, exposing only the SHA-256 fingerprint through the API while keeping private keys encrypted at rest. When you rotate certificates via the /secrets endpoint, the system generates new key pairs but only returns the public fingerprint, ensuring private keys never travel over the network or appear in logs.

What is the difference between SP-initiated and IdP-initiated SSO in Logto?

SP-initiated SSO begins when a user clicks "Log in with SAML" on your application, triggering Logto to send an authentication request to the IdP. IdP-initiated SSO occurs when a user logs into the IdP directly and selects your application from their dashboard, causing the IdP to POST a SAML assertion to Logto's callback URL generated by getSamlAppCallbackUrl in packages/core/src/saml-application/SamlApplication/utils.ts. Both flows converge at the /callback endpoint, but IdP-initiated requires the IdP to know the specific callback URL format.

Can I configure attribute mapping for SAML assertions?

Yes, the attributeMapping field in the SAML application configuration allows you to map IdP claim URIs to Logto user attributes. When creating the application, specify mappings such as "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" in the request body. Logto extracts these attributes from the SAML assertion during callback processing and associates them with the authenticated session according to the mapping defined in SamlApplicationConfigs.

Where can I find integration tests for the SAML application API?

The complete request/response flow is demonstrated in packages/integration-tests/src/tests/api/application/saml-application.test.ts, which validates the CRUD operations, metadata generation, and assertion handling endpoints. These tests serve as executable examples of how the /saml-applications, /metadata, and /callback endpoints interact during real-world SSO scenarios.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →