Identity providers and SCIM
A tenant that already has a directory – Entra ID, Google Workspace, Okta, Keycloak, ADFS … – connects it to kernoid: its people log in there (OpenID Connect or SAML 2.0), and the directory provisions their accounts and groups in kernoid (SCIM 2.0).
Logging in with another provider
Administrators with identity_providers.manage connect a provider (POST /v1/identity-providers, or addIdentityProvider in GraphQL) and route some of the tenant’s verified mail domains to it. Someone who types an address of such a domain in the login UI is sent to the provider instead of being asked for a password.
| Setting | Meaning |
|---|---|
name | shown on the login page |
issuer | OpenID Connect: the provider’s issuer. SAML: its entity ID |
client_id, client_secret | OpenID Connect: kernoid’s client at the provider; the secret is stored sealed and never shown again |
scopes | OpenID Connect: openid is always included; add email and profile to create and link accounts |
saml | SAML: sso_url, certificates (base64 DER or PEM), optionally email_attribute, name_attribute, sign_requests (sign AuthnRequests with kernoid’s key), slo_url (the provider’s single logout service) |
domains | verified mail domains of the tenant whose logins go to this provider; one provider per domain |
auto_create | create an account at the first login |
auto_link | link an existing account with the same address at the first login |
sync_profile | take the display name from the provider at every login |
instance_wide | at creation: a provider for the whole instance (needs instance.manage) |
A provider for the whole instance – one company login for every tenant, say – is created with instance_wide by an instance administrator. The verified domains of any tenant of the instance may be routed to it; accounts are found, linked or created in the tenant whose domain the address is on. Tenants see such a provider in their list (instance_wide: true) but cannot change it, and a domain goes to one provider only.
What to register at the provider is in the provider’s view (GET /v1/identity-providers/<id>):
- OpenID Connect: the redirect URI
https://<instance domain>/idp/callback. - SAML: the Assertion Consumer Service
https://<instance domain>/idp/saml/acs, the entity IDhttps://<instance domain>/idp/samland the single logout servicehttps://<instance domain>/idp/saml/slo– or simply the metadata athttps://<instance domain>/idp/saml/metadata, which also carries kernoid’s certificate (for signed requests and encrypted assertions).
POST /v1/identity-providers/<id>/check tells whether kernoid reaches an OpenID provider as configured (discovery and keys) or can load a SAML provider’s certificates.
Which account
The first login of a person links their identity at the provider (its sub, or the SAML NameID) to an account; later logins find it by that link alone – even if their address at the provider changes.
- With
auto_link, an existing account with the same address is linked; withauto_create, a new one is created. - Both need an address on a verified domain of the tenant that the provider vouches for: OpenID Connect with
email_verified; for SAML, the tenant’s own provider is trusted with the addresses of the tenant’s domains. - Otherwise, if an account of the instance has the provider’s verified address (as its address or verified contact address), linking it is proposed: the person logs in to that account and confirms – kernoid never links on an address alone. Where registration is open they may create a separate account instead.
- A provider with
register(“Sign in with …”) creates an external account – without a mailbox – for a person it does not know, in its tenant or the instance’s registration tenant, if that tenant allows registration. - Otherwise the login is refused – an administrator links the account, or switches one of the options on.
- Logged-in people link further providers from their account page (
POST /v1/account/idp-links).
A login at a provider counts as one factor; if the provider says it used more than one (amr contains mfa, or a multi-factor SAML authentication context), it counts as two – and satisfies a tenant that requires a second factor.
Security
- OpenID Connect: authorization code with PKCE,
stateandnonce; the ID token is verified against the provider’s published keys (RS256/384/512, PS256, ES256/384) – issuer, audience, expiry, nonce. - SAML: XML signatures are verified by bergshamra with its SAML defaults – only the configured certificates count, references must sit where the signature covers them (against XML signature wrapping), duplicate IDs are refused. kernoid accepts no DTDs, takes the one Assertion only if it is signed, and checks issuer,
InResponseTo, destination, recipient, time windows and audience. An encrypted Assertion is decrypted with kernoid’s key of the instance (made at first use, sealed with the main key), then checked the same way. - No session token in a URL: after the provider’s answer, the login UI receives a one-time code in the URL fragment and exchanges it for the session.
- Outbound requests go only to public addresses over HTTPS – checked for the URL, every address DNS gives and every redirect – unless the installation allows private networks (
outbound.allow_private_networks, see Configuration).
Single logout (SAML)
A session that came from a SAML provider with an slo_url logs out there, too: logging out with POST /v1/sessions/current/logout (the login UI’s “Log out”) answers with the provider’s address and kernoid’s signed LogoutRequest; the provider sends the browser back to /idp/saml/slo, and from there to the account page. The other way round, a provider’s LogoutRequest at /idp/saml/slo – signed by one of its certificates, or refused – ends the sessions of that person at kernoid, and kernoid answers with its signed LogoutResponse. Only the HTTP-Redirect binding is supported.
Provisioning with SCIM 2.0
A directory creates, changes and deactivates accounts and groups at https://<instance domain>/scim/v2 (RFC 7643, RFC 7644). Give it a SCIM token: POST /v1/scim-tokens with a name (needs provisioning.manage) – the answer holds the token, shown once, and the endpoint.
| SCIM | kernoid |
|---|---|
User userName | the login name – an address on a verified domain of the tenant |
User emails | further addresses on the tenant’s domains become aliases; others are left out |
User displayName, name | the display name |
User active | deactivates or reactivates the account; deactivating ends its sessions |
DELETE /Users/<id> | deactivates – an account is also a mailbox and owns files; kernoid does not delete it on a directory’s word |
Group displayName, members | a group and its members |
externalId | kept for users and groups |
User password | sets the password (never returned) – not for an account with further permissions; ends its sessions |
Supported: GET, POST, PUT, PATCH, DELETE on /Users and /Groups; filters userName eq "…", externalId eq "…", displayName eq "…"; paging with startIndex and count; sorting with sortBy (userName, displayName, externalId, meta.created) and sortOrder; excludedAttributes=members; /ServiceProviderConfig, /ResourceTypes, /Schemas. PATCH takes the forms Entra ID and Okta send.
- Versions: every resource has
meta.version(a weak ETag, also in theETagheader),meta.createdandmeta.lastModified. PUT, PATCH and DELETE withIf-Matchare refused with412if the resource has changed. - Bulk:
POST /Bulkwith up to 100 operations (1 MiB), run in order;bulkId:<id>refers to a resource made by an earlier operation of the same request,failOnErrorsstops after that many errors.
A SCIM token holds directory.read, accounts.manage and groups.manage – no more. It cannot deactivate an account that holds further permissions (an administrator), nor change the members of a group that holds roles. Changes it makes name it in the audit log (scim:<token id>).