Skip to main content
SAML (Security Assertion Markup Language) enables enterprise single sign-on (SSO) integration with identity providers like Okta, Azure AD, OneLogin, and other SAML 2.0 compliant systems.

Overview

The SAML strategy provides:
  • Enterprise SSO integration
  • SAML 2.0 Service Provider (SP) functionality
  • Support for multiple SAML Identity Providers
  • Assertion-based authentication
  • Organization-specific SAML configurations
SAML support in Ory Kratos is currently in development. The strategy implements basic SAML login, registration, and settings flows.

Configuration

Enable SAML authentication

Configure SAML providers in your Kratos configuration:
kratos.yml
selfservice:
  methods:
    saml:
      enabled: true
      config:
        providers:
          - id: okta
            provider: saml
            label: Okta SSO
            entity_id: https://your-app.com/saml/metadata
            sso_url: https://your-okta.okta.com/app/appid/sso/saml
            certificate: |
              -----BEGIN CERTIFICATE-----
              MIIDpDCCAoygAwIBAgIGAXoTp...
              -----END CERTIFICATE-----
            mapper_url: file:///etc/config/kratos/saml.okta.jsonnet

SAML provider configuration

Each SAML provider requires:
providers:
  - id: okta
    provider: saml
    label: Okta SSO
    entity_id: https://your-app.com/saml/metadata
    sso_url: https://your-okta.okta.com/app/xyz/sso/saml
    certificate: |
      -----BEGIN CERTIFICATE-----
      ...
      -----END CERTIFICATE-----
    mapper_url: file:///etc/config/saml.okta.jsonnet
Configuration parameters:
  • id - Unique identifier for this SAML provider
  • provider - Must be saml
  • label - Display name shown to users
  • entity_id - Your application’s SAML entity ID (Service Provider)
  • sso_url - Identity Provider’s SSO URL
  • certificate - IdP’s public certificate for validating assertions
  • mapper_url - Jsonnet file for mapping SAML attributes to identity traits

Identity schema configuration

SAML doesn’t require specific identity schema configuration, but you should define traits that will be populated from SAML assertions:
identity.schema.json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "traits": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string",
          "format": "email",
          "title": "E-Mail"
        },
        "name": {
          "type": "object",
          "properties": {
            "first": {
              "type": "string",
              "title": "First Name"
            },
            "last": {
              "type": "string",
              "title": "Last Name"
            }
          }
        },
        "groups": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "required": ["email"]
    }
  }
}

SAML attribute mapping

Basic mapper

Use Jsonnet to map SAML assertion attributes to identity traits:
saml.okta.jsonnet
local assertions = std.extVar('assertions');

{
  identity: {
    traits: {
      email: assertions.email,
      name: {
        first: assertions.firstName,
        last: assertions.lastName,
      },
    },
  },
}

Advanced mapper with groups

saml.azure.jsonnet
local assertions = std.extVar('assertions');

{
  identity: {
    traits: {
      email: assertions['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'],
      name: {
        first: assertions['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname'],
        last: assertions['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'],
      },
      groups: if std.objectHas(assertions, 'groups') then
        assertions.groups
      else
        [],
    },
  },
}
SAML attribute names vary by identity provider. Check your IdP’s documentation for the exact attribute names.

User flows

Login with SAML

1

Initialize login flow

Create a login flow:
curl -X GET https://your-kratos-instance/self-service/login/browser
2

Initiate SAML authentication

User selects SAML provider and is redirected:
curl -X POST https://your-kratos-instance/self-service/login?flow=<flow-id> \
  -H "Content-Type: application/json" \
  -d '{
    "method": "saml",
    "provider": "okta"
  }'
Kratos redirects to the IdP’s SSO URL with a SAML AuthnRequest.
3

IdP authentication

User authenticates with their identity provider (Okta, Azure AD, etc.).
4

SAML assertion callback

IdP sends SAML assertion back to Kratos:
POST https://your-kratos-instance/self-service/methods/saml/callback
The assertion contains user attributes and authentication proof.
5

Session created

Kratos validates the assertion and creates a session.

Registration with SAML

The registration flow is identical to login (see selfservice/strategy/saml/registration.go):
curl -X POST https://your-kratos-instance/self-service/registration?flow=<flow-id> \
  -H "Content-Type: application/json" \
  -d '{
    "method": "saml",
    "provider": "okta"
  }'
If the user doesn’t exist, a new identity is created using the SAML attributes.

Account linking

Users can link SAML providers through settings (see selfservice/strategy/saml/settings.go):
curl -X POST https://your-kratos-instance/self-service/settings?flow=<flow-id> \
  -H "Content-Type: application/json" \
  -H "Cookie: ory_kratos_session=<session-token>" \
  -d '{
    "method": "saml",
    "link": "azure-ad"
  }'

SAML protocol details

AuthnRequest

When initiating SAML authentication, Kratos generates a SAML AuthnRequest:
<samlp:AuthnRequest
  xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
  xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
  ID="_abc123"
  Version="2.0"
  IssueInstant="2024-01-15T10:00:00Z"
  Destination="https://idp.example.com/sso"
  AssertionConsumerServiceURL="https://your-kratos-instance/self-service/methods/saml/callback">
  <saml:Issuer>https://your-app.com/saml/metadata</saml:Issuer>
</samlp:AuthnRequest>

SAML Response

The IdP returns a signed SAML Response with assertions:
<samlp:Response
  xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
  ID="_def456"
  Version="2.0"
  IssueInstant="2024-01-15T10:00:30Z"
  Destination="https://your-kratos-instance/self-service/methods/saml/callback">
  <saml:Issuer>https://idp.example.com</saml:Issuer>
  <samlp:Status>
    <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </samlp:Status>
  <saml:Assertion>
    <saml:Subject>
      <saml:NameID>user@example.com</saml:NameID>
    </saml:Subject>
    <saml:AttributeStatement>
      <saml:Attribute Name="email">
        <saml:AttributeValue>user@example.com</saml:AttributeValue>
      </saml:Attribute>
      <saml:Attribute Name="firstName">
        <saml:AttributeValue>John</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>

Security considerations

Certificate validation

Always validate SAML assertions using the IdP’s public certificate. Never disable signature validation in production.
Kratos validates:
  1. Assertion signatures using the configured certificate
  2. Response signatures
  3. Assertion timing (NotBefore, NotOnOrAfter)
  4. Audience restrictions
  5. Recipient validation

Replay protection

SAML assertions include:
  • Unique ID - Each assertion has a unique identifier
  • Timestamp validation - NotBefore and NotOnOrAfter conditions
  • One-time use - Assertions should only be processed once
Implement assertion ID tracking to prevent replay attacks.

Redirect validation

kratos.yml
selfservice:
  allowed_return_urls:
    - https://your-app.com
    - https://your-app.com/dashboard
Always configure allowed return URLs to prevent open redirects.

Service Provider metadata

Generate SP metadata

Kratos can serve SAML Service Provider metadata for IdP configuration:
<EntityDescriptor
  xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
  entityID="https://your-app.com/saml/metadata">
  <SPSSODescriptor
    AuthnRequestsSigned="false"
    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://your-kratos-instance/self-service/methods/saml/callback"
      index="0"/>
  </SPSSODescriptor>
</EntityDescriptor>
Provide this metadata to your IdP administrator.

Organization-specific SAML

Configure different SAML providers per organization:
kratos.yml
selfservice:
  methods:
    saml:
      enabled: true
      config:
        providers:
          - id: company-a-okta
            provider: saml
            organization: org_company_a
            label: Company A SSO
            entity_id: https://your-app.com/saml/metadata/company-a
            sso_url: https://company-a.okta.com/sso/saml
            certificate: ...
          - id: company-b-azure
            provider: saml
            organization: org_company_b
            label: Company B SSO
            entity_id: https://your-app.com/saml/metadata/company-b
            sso_url: https://login.microsoftonline.com/tenant-b/saml2
            certificate: ...

Common SAML attributes

Standard attributes

Common SAML attribute names:
AttributeDescriptionExample
emailEmail addressuser@example.com
firstName / givenNameFirst nameJohn
lastName / surnameLast nameDoe
displayNameFull nameJohn Doe
groupsUser groups[“admin”, “users”]
employeeIdEmployee IDE12345
departmentDepartmentEngineering

Azure AD attributes

Azure AD uses URN-style attribute names:
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
http://schemas.microsoft.com/identity/claims/displayname

API reference

Strategy implementation

The SAML strategy implements:
  • Login flow - See selfservice/strategy/saml/login.go
  • Registration flow - See selfservice/strategy/saml/registration.go
  • Settings flow - See selfservice/strategy/saml/settings.go

Update request structure

The SAML login/registration request (from selfservice/strategy/saml/login.go:11-31):
type UpdateFlowRequest struct {
    // Provider to authenticate with (required)
    Provider string `json:"provider"`
    
    // CSRF Token
    CSRFToken string `json:"csrf_token"`
    
    // Method must be "saml"
    Method string `json:"method"`
    
    // Transient payload for webhooks
    TransientPayload json.RawMessage `json:"transient_payload,omitempty"`
}

Configuration reference

selfservice:
  methods:
    saml:
      enabled: true
      config:
        providers:
          - id: okta
            provider: saml
            label: Okta SSO
            entity_id: https://your-app.com/saml/metadata
            sso_url: https://your-okta.okta.com/sso/saml
            certificate: |
              -----BEGIN CERTIFICATE-----
              ...
              -----END CERTIFICATE-----
            mapper_url: file:///etc/config/saml.jsonnet

Troubleshooting

Clock skew issues

If assertions are rejected due to timing:
  1. Ensure server clocks are synchronized (use NTP)
  2. Check NotBefore and NotOnOrAfter in assertions
  3. Allow for reasonable clock skew (typically 60 seconds)

Certificate errors

If signature validation fails:
  1. Verify the certificate is correctly formatted
  2. Ensure the certificate matches the one in IdP
  3. Check certificate expiration
  4. Validate certificate chain if using intermediate CAs

Missing attributes

If expected attributes are missing:
  1. Check IdP attribute mapping configuration
  2. Verify attribute names in Jsonnet mapper
  3. Ensure IdP is configured to release attributes
  4. Check assertion XML for available attributes

Next steps

Build docs developers (and LLMs) love