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:
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:
{
"$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:
local assertions = std. extVar ( 'assertions' );
{
identity: {
traits: {
email: assertions.email,
name: {
first: assertions.firstName,
last: assertions.lastName,
},
},
},
}
Advanced mapper with groups
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
Initialize login flow
Create a login flow: curl -X GET https://your-kratos-instance/self-service/login/browser
Initiate SAML authentication
User selects SAML provider and is redirected: curl -X POST https://your-kratos-instance/self-service/login?flow= < flow-i d > \
-H "Content-Type: application/json" \
-d '{
"method": "saml",
"provider": "okta"
}'
Kratos redirects to the IdP’s SSO URL with a SAML AuthnRequest.
IdP authentication
User authenticates with their identity provider (Okta, Azure AD, etc.).
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.
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-i d > \
-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-i d > \
-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:
Assertion signatures using the configured certificate
Response signatures
Assertion timing (NotBefore, NotOnOrAfter)
Audience restrictions
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
selfservice :
allowed_return_urls :
- https://your-app.com
- https://your-app.com/dashboard
Always configure allowed return URLs to prevent open redirects.
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:
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:
Attribute Description Example emailEmail address user@example.com firstName / givenNameFirst name John lastName / surnameLast name Doe displayNameFull name John Doe groupsUser groups [“admin”, “users”] employeeIdEmployee ID E12345 departmentDepartment Engineering
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
Single provider
Multiple providers
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:
Ensure server clocks are synchronized (use NTP)
Check NotBefore and NotOnOrAfter in assertions
Allow for reasonable clock skew (typically 60 seconds)
Certificate errors
If signature validation fails:
Verify the certificate is correctly formatted
Ensure the certificate matches the one in IdP
Check certificate expiration
Validate certificate chain if using intermediate CAs
Missing attributes
If expected attributes are missing:
Check IdP attribute mapping configuration
Verify attribute names in Jsonnet mapper
Ensure IdP is configured to release attributes
Check assertion XML for available attributes
Next steps