SSO & SCIM
Enterprise (sso entitlement). Per-organization single sign-on via SAML 2.0 or OIDC, plus SCIM 2.0 for user and group provisioning from your IdP (Okta, Entra ID, and compatible).
SSO issues the same httpOnly access_token cookie as password login — there is no parallel session mechanism, and everything downstream (RBAC, teams, audit) works identically.
Configuration
Each org has one SSO config, managed by org owners/admins under Settings or via the API:
# Read current config (secrets masked; includes computed loginUrl and scimBaseUrl)
curl /sso/settings
# Configure OIDC with JIT provisioning
curl -X PUT /sso/settings \
-H "Content-Type: application/json" \
-d '{
"protocol": "oidc",
"enabled": true,
"oidcIssuerUrl": "https://login.example.com",
"oidcClientId": "almyty",
"oidcClientSecret": "…",
"jitProvisioning": true,
"defaultRole": "member"
}'| Field | Notes |
|---|---|
protocol | saml | oidc |
enabled | master switch for the login endpoints |
SAML: samlEntryPoint, samlIssuer, samlCert | IdP SSO URL, entity ID, signing cert |
OIDC: oidcIssuerUrl, oidcClientId, oidcClientSecret | discovery-based; secret is encrypted at rest |
jitProvisioning | create unknown users on first login (see below) |
defaultRole | org role for JIT-provisioned users (default member) |
Set PUBLIC_API_URL on the backend so generated URLs (SAML ACS, metadata, SCIM base, login links) point at your public API origin rather than an internal hostname.
Login endpoints
Per-org, unauthenticated by design:
| Endpoint | Purpose |
|---|---|
GET /sso/:orgId/saml/login | redirect to the IdP |
POST /sso/:orgId/saml/callback | SAML ACS (assertion consumer) |
GET /sso/:orgId/saml/metadata | SP metadata XML for IdP setup |
GET /sso/:orgId/oidc/login | start the OIDC flow (state cookie, 10 min) |
GET /sso/:orgId/oidc/callback | code exchange, then cookie + redirect |
On a verified assertion the user is resolved by email:
- Existing active member → logged in.
- Deactivated member → 401.
- Unknown user → created with the org’s
defaultRole— but only ifjitProvisioningis on. Otherwise 401, deferring membership to SCIM or invites.
SCIM provisioning
SCIM lets your IdP drive the member list instead of (or alongside) JIT. Generate a bearer token:
curl -X POST /sso/settings/scim-token
# → { "token": "scim_…" } — also enables SCIM for the orgPoint your IdP at the SCIM base URL from GET /sso/settings and use the token as the bearer credential.
Supported operations under /scim/v2 (authenticated by the SCIM token, scoped to your org):
- Users:
POST,GET(list; filter support is limited touserName eq "…"),GET/PUT/PATCH /Users/:id,DELETE /Users/:id - Groups:
POST,GET,GET/PATCH /Groups/:id,DELETE /Groups/:id— groups map to almyty teams
Deprovisioning (PATCH active: false or DELETE) deactivates the user’s org membership, not their account — they lose access to your org but their user survives if they belong to other orgs. Both Okta and Entra PATCH payload shapes are handled.