Create SSO Configuration
Create a new SSO connection for the caller's active account. Once a matching config is enforced and (for custom IdPs) domain-verified, account members whose email addresses match the configured email_domain are forced through your IdP at login instead of using password auth.
Plan-gated: requires the account's sso entitlement (else 403). Permission-gated: requires the settings:write account permission — members without it get 403.
Each email_domain can have at most one SSO config per account. Adding a second one for the same domain returns 409.
Provider types usable at login: google, github, and oidc (custom OIDC — Okta, Microsoft Entra ID, Auth0, or any OIDC-compliant IdP). SAML is not supported: provider_type: "saml" is refused with 400 “SAML sign-in is not supported. Use OpenID Connect (oidc), Google or GitHub instead.” Use oidc.
After creation, run POST /v1/sso/config/{uuid}/test to sanity-check the config. For oidc providers you must also complete domain verification before the config applies at login (see below).
Request body
@<email_domain> hit this config. Must be unique within the account.google, github, oidc. For google / github the OAuth endpoints are fixed (see below); oidc is a custom IdP that requires its own endpoint URLs. saml returns 400 (SAML is not supported).400 (detail: client_secret is required). Encrypted at rest; never returned by any GET endpoint. On update, sending a blank client_secret keeps the existing secret.provider_type: "oidc". Ignored for google / github — the platform force-locks those to Google's / GitHub's canonical endpoints, overriding any values you send (a fake-IdP guard). Must be https and resolve to a public host; internal/private hosts (link-local, RFC1918, cluster-internal) are rejected with 400.provider_type: "oidc". The IdP's OIDC issuer identifier (e.g. https://acme.okta.com/oauth2/default). At login, ngris matches the iss claim of the provider's signed id_token against this value exactly. Ignored and cleared for google / github (they use platform-controlled endpoints — GitHub issues no id_token). Must be https and resolve to a public host. Tip: use POST /v1/sso/config/discover to fetch it from your provider's .well-known/openid-configuration.provider_type: "oidc". The provider's JWKS endpoint (e.g. https://acme.okta.com/oauth2/default/v1/keys). At login, ngris fetches these public keys to verify the id_token signature. Ignored and cleared for google / github. Must be https and resolve to a public host. Discover returns this as jwks_uri.openid profile email (user:email for github).true, account members whose email domain matches are forced through SSO — their password login is rejected. Requires the account's plan to include the sso entitlement or the request is refused with 403. Default false (opt-in SSO).How custom OIDC identity is verified at login
For oidc, ngris does not trust the identity returned by the user-info body. At login it cryptographically verifies the provider's signed id_token before creating a session: the signature is checked against the keys at jwks_url (asymmetric algorithms only), and the token's iss (must equal issuer), aud (must contain your client_id), exp (not expired) and the login nonce are all validated. That is why issuer and jwks_url are required for oidc — a config without them cannot be used at login, so the API rejects it at save. google / github need neither (their identity comes from platform-controlled endpoints; GitHub issues no id_token).
Auto-filling endpoints (Discover)
Rather than looking up each URL by hand, call POST /v1/sso/config/discover with your provider's issuer URL. It reads the IdP's .well-known/openid-configuration and returns authorization_endpoint, token_endpoint, userinfo_endpoint, issuer, and jwks_uri — which map onto auth_url, token_url, userinfo_url, issuer, and jwks_url. The dashboard's Discover button does this automatically, so an admin normally pastes one issuer URL and the rest is filled in.
Domain verification (oidc)
Before a custom oidc config applies at login, the account must prove it owns email_domain. On create (and whenever email_domain changes) the config is returned with domain_verified: false plus a DNS-TXT challenge in domain_verify_record_name / domain_verify_record_value. Publish that TXT record, then call POST /v1/sso/config/{uuid}/verify-domain — on an exact match domain_verified flips to true.
google / github configs are auto-verified (identity is asserted by the provider and the endpoints are locked), so they carry no TXT challenge and apply immediately.
Response (200 OK)
The newly-created SSO config (secret never included). Fields: uuid, account_uuid, email_domain, provider_type, client_id, auth_url, token_url, userinfo_url, issuer (oidc), jwks_url (oidc), scopes, status (active | error), enforced, domain_verified, domain_verify_record_name (omitted once verified / for google+github), domain_verify_record_value (same), last_error_message, last_tested_at, created_at, updated_at.
Deprecated: until the sunset this response also carries the legacy integer id and *_id fields. Don't use them; read uuid and the *_uuid fields.
Errors
client_secret), invalid provider_type (including saml: “SAML sign-in is not supported. Use OpenID Connect (oidc), Google or GitHub instead.”), missing auth_url/token_url/userinfo_url for oidc, missing issuer/jwks_url for oidc, or a non-https / internal endpoint URL.settings:write, the account's plan doesn't include SSO, or enforced: true was sent for a plan without the SSO entitlement.email_domain already exists in the account.