SAML SSO
Enterprise single sign-on via SAML 2.0. Supports both a global IdP configuration and per-organization IdP setup with email domain routing. Built on the onelogin/php-saml package.
SSO Modes
The sso.mode setting controls how SSO operates. It can be set to one of three values:
disabled— SSO is turned off. All SAML routes return403 Forbidden.global— A single IdP is configured for the entire application. All users authenticate through the same identity provider. Configuration is stored inAppSettings.per_org— Each organization can configure its own IdP. Users are routed to the correct IdP based on their email domain.
Global SSO
In global mode, a single identity provider is configured from the admin panel. The IdP metadata (Entity ID, SSO URL, SLS URL, X.509 certificate) is stored in the application's AppSettings.
Admin Settings → Authentication → SAML SSO
├── IdP Entity ID
├── IdP SSO URL
├── IdP SLS URL (optional)
├── IdP X.509 Certificate
├── NameID Format
└── Attribute Mapping
Per-Organization SSO
In per-org mode, each organization manages its own SAML configuration.
SamlConfiguration Model
Each organization can have one SamlConfiguration record:
saml_configurations
├── id
├── organization_id
├── idp_entity_id
├── idp_sso_url
├── idp_sls_url (nullable)
├── idp_x509_certificate
├── name_id_format (default: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress)
├── attribute_mapping (JSON)
├── force_sso (boolean, default: false)
├── is_active (boolean)
├── created_at
└── updated_at
Email Domain Routing
The OrgDomain model maps verified email domains to organizations. When a user enters their email on the login page, the system checks the domain to determine which organization's IdP to use.
org_domains
├── id
├── organization_id
├── domain (e.g., "acme.com")
├── verified_at (nullable)
├── verification_token
├── created_at
└── updated_at
Domain Verification
Organizations must verify ownership of their domain before it can be used for SSO routing. Verification is done by adding a TXT record to the domain's DNS:
TXT _saaskitfy-verify.acme.com "saaskitfy-verify=abc123def456"
The system checks for the TXT record and sets verified_at on success. Unverified domains cannot be used for SSO routing.
SAML Routes
SP Metadata
GET /api/auth/saml/{orgSlug}/metadata
Returns the Service Provider metadata XML. Provide this URL to your IdP during setup. In global mode, use global as the {orgSlug}.
Initiate Login
GET /api/auth/saml/{orgSlug}/login
Redirects the user to the IdP's SSO URL with a SAML AuthnRequest.
Assertion Consumer Service (ACS)
POST /api/auth/saml/{orgSlug}/acs
Receives the SAML Response from the IdP. Validates the assertion, extracts user attributes, and issues a Sanctum token.
Single Logout Service (SLS)
GET /api/auth/saml/{orgSlug}/sls
Handles IdP-initiated logout. Revokes the user's Sanctum token and redirects to the login page.
Attribute Mapping
SAML attributes from the IdP response are mapped to user fields. The mapping is configurable per IdP:
{
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"first_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
"last_name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
}
If name is not provided but first_name and last_name are, the name is constructed by concatenating them.
NameID Format
The NameID format specifies how the user identifier is sent in the SAML assertion. Configurable per IdP with the following options:
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress(default)urn:oasis:names:tc:SAML:2.0:nameid-format:persistenturn:oasis:names:tc:SAML:2.0:nameid-format:transienturn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
JIT Provisioning
When a user authenticates via SAML for the first time and no account exists with that email:
- A new user account is created with the attributes from the SAML assertion
- The email is automatically marked as verified
- In per-org mode, the user is automatically added to the organization as a
member - No password is set — the user authenticates exclusively via SSO
Force SSO
When force_sso is enabled on a SAML configuration, users belonging to that organization (or matching a verified domain) must authenticate via SSO. Password login, magic link, and OAuth are blocked for these users. This is enforced at the login endpoint level.
Admin Toggle
SSO mode is controlled from the admin panel:
- Setting:
sso.mode→disabled,per_org, orglobal - Switching from
per_orgtoglobaldoes not delete per-org configurations — they are simply ignored - Switching to
disabledblocks all SAML routes but preserves all configuration data