Skip to main content

SAML sign-in

A self-host can sign people in through a SAML 2.0 identity provider (Okta, Entra ID, or any other). OIDC is the other option, see OIDC authentication. SAML is off until you turn it on.

What works: sign in started from Align (SP-initiated), group to role mapping, and SCIM provisioning alongside it. What does not: sign in started from the IdP's own app tile (IdP-initiated) is refused on purpose.

If you set a default tenant (step 1), the Align sign-in page shows a Sign in with SAML button once that tenant has an identity provider. Without a default tenant there is no button, and people start at https://<your-api-host>/saml/login?tenant=<tenant id or subdomain>, for example from a bookmark or an IdP "bookmark app" that points there.

This has been tested in the gateway's test suite with signed assertions from a throwaway identity provider. It has not been run against a live Okta or Entra tenant.

1. Turn it on in the chart​

gateway:
apiBaseUrl: https://api.corp.example # required: the IdP checks the ACS URL exactly
saml:
enabled: true
groupsAttribute: groups # optional, the assertion attribute that lists groups

The chart renders SAML_ENABLED, SAML_SP_ENTITY_ID (default <apiBaseUrl>/saml/metadata), SAML_SP_ACS_URL (default <apiBaseUrl>/saml/acs) and SAML_GROUPS_ATTRIBUTE. With enabled: false, nothing renders and the gateway registers no /saml route. Turning it on with only the placeholder ingress host fails the render. Set spAcsUrl and spEntityId yourself if the public URL differs from apiBaseUrl.

Set the default tenant if this install has one organization signing in with SAML:

gateway:
saml:
defaultTenantId: 11111111-1111-4111-8111-111111111111 # the tenant uuid people sign into

This renders SAML_DEFAULT_TENANT_ID. Unset, there is no SAML button and /saml/login without ?tenant= answers 400. The sign-in page asks the gateway one question about that tenant only, at GET /auth/saml/available. The call takes no input, answers {"saml": true} or {"saml": false}, and is limited to 30 a minute per address. It cannot be used to ask which other tenants or domains have an identity provider. There is no lookup by email domain or subdomain on this path.

Above one gateway replica you need Redis (gateway.redis), as for sessions. A login records its request id in the gateway cache, and a second pod must see it.

Map groups to roles with SSO_GROUP_ROLE_MAPPING, see Map directory groups to roles.

2. Register Align in your IdP​

Download the service provider metadata from https://<your-api-host>/saml/metadata, or enter the values by hand:

IdP fieldValue
ACS / Reply / Single sign-on URLhttps://<your-api-host>/saml/acs
Entity ID / Audiencehttps://<your-api-host>/saml/metadata
Name IDThe user's email address
Assertion signingSign the assertion (required)
Attributesemail, and a groups attribute if you map roles

3. Tell Align about the IdP​

Sign in as an organization admin and open Settings, Single sign-on. Under Identity provider (SAML) enter the entity ID (issuer), the single sign-on URL (https only) and the IdP's public signing certificate, then save. On a saved identity provider you can leave the certificate blank to keep the one stored. The page shows the certificate's SHA-256 fingerprint, the same value your IdP shows, so you can check it by eye.

The gateway refuses, and writes nothing, when:

  • SAML is not turned on for this gateway (step 1). With no /saml route a saved identity provider could not sign anyone in, and it would make the organization look ready for SSO enforcement.
  • The certificate is a private key, is not one X.509 certificate (a bundle or trailing bytes are refused), has expired, is not valid yet, or is an RSA key shorter than 2048 bits.
  • SSO enforcement is on and a SAML identity provider is already stored. Turn enforcement off first, change the values, check a SAML sign-in, then turn it back on. A wrong certificate with enforcement on would lock everyone out.
  • You try to turn SSO enforcement on with SAML as the only identity provider and this install has no default SAML tenant (step 1) for your organization. People would have no SAML button to start from.
  • SSO enforcement is on and nothing is connected, or the organization's SSO has been switched off and already has an identity provider. Both need Align or the database owner.

Saving writes an audit entry with the certificate's fingerprint, never the certificate. It switches SAML on for a tenant that had nothing connected, and it never switches SSO enforcement on: turn that on yourself once SAML works for an admin, so you cannot lock yourself out while testing.

The same settings are available over GET and PUT /auth/admin/saml with an interactive admin login. API tokens, service tokens and demo mode are refused.

The earlier way still works, run once as the database owner:

INSERT INTO tenant_sso_config
(tenant_id, provider_type, subdomain, enabled, enforce_sso,
saml_entity_id, saml_sso_url, saml_certificate)
VALUES
('<tenant uuid>', 'saml', '<short name>', true, false,
'<IdP entity id / issuer>',
'<IdP SSO URL>',
'<IdP signing certificate, base64, no BEGIN/END lines>')
ON CONFLICT (tenant_id) DO UPDATE SET
provider_type = 'saml', enabled = true,
saml_entity_id = EXCLUDED.saml_entity_id,
saml_sso_url = EXCLUDED.saml_sso_url,
saml_certificate = EXCLUDED.saml_certificate;

A row without a certificate is ignored, so SAML sign-in fails closed.

What the gateway checks​

A response signs nobody in unless all of these hold. Each one is covered by a test with a refused case and an accepted case.

  • The assertion is signed with the certificate you stored. An unsigned assertion, or one signed by another key, is refused.
  • The audience is your entity id, and the Recipient is your ACS URL.
  • The issuer is the IdP entity id you stored.
  • The response answers a login that Align started, once. A response with no request id (IdP-initiated), one for another login, or one posted a second time is refused.
  • It is inside its validity window, with 60 seconds of clock difference allowed.
  • The tenant comes from a server-side record created at /saml/login, never from the response.
  • A deactivated user (for example removed by SCIM) is refused.

On success Align creates or updates the user, applies the group to role mapping, and sets the same signed session cookie as other sign-ins.

Check it​

  1. Open the Align sign-in page and choose Sign in with SAML (needs the default tenant), or open https://<your-api-host>/saml/login?tenant=<tenant id or subdomain>. You land on the IdP.
  2. Sign in. You come back to the app signed in as that user.

If sign-in fails, the gateway log has one line, [SAML] Sign-in refused:, with the reason. The browser only sees a generic message.