Enterprise SSO connects 24Observe to your existing identity provider over generic OpenID Connect. Any spec-compliant IdP works — Okta, Azure AD, Google Workspace, Auth0, OneLogin. Your team signs in through the provider you already trust, users are provisioned automatically on first login, and offboarding stays where it belongs: in your IdP.
Each organization connects one IdP. Setup is owner-only and lives in the dashboard under Settings → Enterprise SSO (OIDC).
https://api.24observe.com/api/v1/auth/sso/callbackThe same callback is used for every organization — you only ever register this one URI.
https://your-org.okta.com. 24Observe discovers the IdP's endpoints and signing keys from here.acme.com. When set, only identities whose email ends in @acme.com may sign in. Leave blank to allow any identity the IdP authenticates.admin, member, or viewer. SSO never grants owner.The Settings page surfaces two values. Register the first in your IdP app; hand the second to your team.
https://api.24observe.com/api/v1/auth/sso/callback
https://api.24observe.com/api/v1/auth/sso/<your-org-slug>/start
302 to your IdP's authorization endpoint.id_token against the IdP's JWKS — signature, issuer, audience, and nonce all must match.
24Observe never stores the IdP's access or refresh token. We use the
verified id_token to establish identity, then issue our own
session — nothing more.
For Azure AD, use the tenant-specific issuer so audience and issuer validation are unambiguous:
https://login.microsoftonline.com/<tenant-id>/v2.0
The multi-tenant /common endpoint is intentionally not
supported — bind the connection to your tenant.
The same connection is manageable over the API with an owner PAT or a full-access owner session. The secret is never returned on read.
GET /api/v1/settings/sso — returns the connection without the client secret, plus the team loginUrl.PUT /api/v1/settings/sso — create or update. Body: { issuer, clientId, clientSecret?, enabled?, defaultRole?, allowedDomain? }. Omit clientSecret to keep the stored one.DELETE /api/v1/settings/sso — remove the connection.curl -X PUT https://api.24observe.com/api/v1/settings/sso \
-H 'Authorization: Bearer obs_<owner>' \
-H 'Content-Type: application/json' \
-d '{
"issuer": "https://acme.okta.com",
"clientId": "0oa1b2c3d4e5f6g7h8i9",
"clientSecret": "<from your IdP app>",
"defaultRole": "member",
"allowedDomain": "acme.com",
"enabled": true
}'
# Read it back — note the absent secret and the surfaced loginUrl
curl https://api.24observe.com/api/v1/settings/sso \
-H 'Authorization: Bearer obs_<owner>'
# Response (200)
{
"issuer": "https://acme.okta.com",
"clientId": "0oa1b2c3d4e5f6g7h8i9",
"enabled": true,
"defaultRole": "member",
"allowedDomain": "acme.com",
"loginUrl": "https://api.24observe.com/api/v1/auth/sso/acme/start"
} id_token.id_token is verified against the IdP's JWKS on every login — signature, issuer, audience, and nonce.owner role. Ownership stays a deliberate, human action.