Skip to main content

Identity Federation and SSO

Connecting an existing identity provider is usually the first integration a customer does, and System9 treats it as a first-class, governed object rather than a configuration file. A federated provider is a registry row with a lifecycle, a required conformance test before it can be switched on, its own audit chain, and its own replication policy.

The design commitment underneath is worth stating first, because everything else follows from it:

Federation is additive. Local authentication is the baseline, not a failure mode. A deployment with zero providers is a complete, fully functional deployment. A deployment with ten providers still has a local administrator path that no policy, and no enforcement mode, can remove.

Why this is more than a login page​

The login screen is the least interesting part. What matters is that a federated person lands as a directory entity, byte-identical to one created in the console, and therefore shares one authorization model with every machine, device and agent in the estate.

The customer keeps their identity provider and their joiner-mover-leaver process. What they gain is that their people, their service accounts, their devices and their agents are all principals in the same directory, evaluated by the same grant machinery. A federated user and an agent acting on their behalf resolve into one delegation chain, which is the part that does not exist if identity and agent authority live in different systems.

What you can connect​

Two orthogonal choices describe a provider: the protocol (the engine that runs the exchange) and the kind (the vendor preset that pre-fills claim defaults).

Provider kindProtocolNotes
Microsoft Entra IDOIDCFull flow with a vendor claim preset; multi-tenant issuer templates supported
OktaOIDCVendor claim preset. Note Okta conventionally carries roles on the scp claim, not roles
Google CloudOIDCVendor claim preset
Ping IdentityOIDCRuns on the generic OIDC engine
AWS IAM Identity CenterOIDCRuns on the generic OIDC engine
Generic OIDCOIDCAnything else that speaks OIDC, including Auth0, Keycloak, JumpCloud and OneLogin
Generic SAML 2.0SAMLFull assertion-consumer, metadata and single-logout surface
Active Directory / LDAPLDAPDirect bind against a domain controller. A credential form on the login page rather than a redirect, and the provider is resolved server-side, so one form covers every configured directory
CAC / PIVDirect mTLSSmartcard authentication without an identity provider in the path. Export-controlled; see CAC and PIV

A SAML deployment of Entra ID, Okta or Ping registers as Generic SAML 2.0, there is no vendor-specific SAML kind.

Providers are scoped either org (replicates to every node in the organization) or node (local to one node). Scope, organization, node and slug are fixed at creation and cannot be edited afterwards.

The shape of a federated login​

Figure: a federated login, end to end. Cyan stages carry short-lived single-use material; the two amber stages are gates that can refuse and are the only way to reach the green stage. No session exists until every gate has passed. Ceiling: the diagram shows the OIDC path; SAML substitutes assertion validation for token validation at the same choke point and is otherwise identical from the match stage onward.

Table equivalent

StageWhat happensRefuses when
Loginstate, nonce and a PKCE S256 challenge are minted and storedThe return-to target is not a relative path
Identity providerThe person authenticates at their own IdPPer the IdP's own policy
Callbackstate is consumed by an atomic single-use deleteThe state is unknown or already used, or the echoed issuer does not match
ValidationSignature, algorithm, issuer, tenant, audience, authorized party, nonceAny one of them fails; see validation
Match or provisionThe matching policy classifies the subject, then the admission gate runsNo match under a reject policy, or the admission gate denies
Assurance floorThe returned authentication context is compared to the effective floorThe context does not clear the floor
SessionA session is minted on the same path local login usesDoes not refuse

What the validation gate checks​

Validation runs before any session is minted, and it never degrades. It is a single choke point rather than checks scattered through the router, so there is no path to a session that skips it.

CheckBehaviour
Signature algorithmAsymmetric only (RS256, ES256, PS256). HS* and none are never accepted, which closes the algorithm-confusion class
Key sourceKeys resolve only from the pinned JWKS endpoint, by key id
Embedded key headersTokens carrying jku, jwk, x5u or x5c are rejected outright
SizeA 64 KiB pre-parse cap on tokens, 256 KiB on SAML responses
IssuerMust match the configured issuer, including the concrete instantiation of a multi-tenant template
TenantThe tenant claim must appear in the allowed-tenant list when one is configured
Audience and authorized partyBoth checked
NonceMust be present and match the one minted at login
Clock skewTolerated to a configured leeway, 60 seconds by default

SAML adds an XML signature-wrapping defence that asserts exactly one assertion in a validated protocol envelope, single-use InResponseTo correlation, an assertion replay store, and audience, recipient, destination and time-window checks. If the replay store is unavailable the login is refused, rather than proceeding unchecked.

The provider lifecycle, and the gate that guards it​

A provider moves through six states, and the transitions are enumerated rather than implied.

Figure: the provider lifecycle. A provider can only be deleted from draft or archived, never while it might be carrying logins. Any edit to the configuration of an enabled provider drops it back to testing, and it cannot return to enabled without a fresh passing test over the edited configuration.

Table equivalent

StateMeaningCan it serve logins?
DraftCreated, configuration incompleteNo
TestingConfigured, awaiting a passing conformance testNo
EnabledServingYes
DisabledSwitched off; provider-bound sessions were drained on the wayNo
DecommissioningBeing retired, reversibleNo
ArchivedRetired, record retained for auditNo

The enable gate​

A provider becomes enabled only when a stored test result exists that

  1. passed,
  2. ran more recently than the last change to the core configuration, and
  3. was run against a configuration hash equal to the hash of the configuration as it stands now.

The third condition is the interesting one. Without it, an operator could pass a test, edit the configuration, and enable the edited version on the strength of a test that never saw it. Hashing the canonical configuration into the test result closes that window.

The practical consequence for operators: editing an enabled provider takes it out of the login set. The edit is accepted, the state drops to testing, and re-enabling reports a configuration-hash mismatch until a fresh test is run. An operator who tweaks a scope list and walks away has silently removed a login path, so treat a configuration edit as a change window.

Mapping an identity to an entity​

A federated subject becomes a directory entity through three declarations, each stored per provider.

Claim mapping and the identity key​

Claim mappings translate what the provider asserts into platform attributes: entity_match_key, email, display_name, given_name, family_name, groups, roles. Transforms such as lowercasing or stripping a domain prefix are applied on the way through.

Exactly one mapping per provider is the identity key, and it must be over an immutable claim. Email is rejected as an identity key, deliberately: email addresses are reassigned, and an identity key that can be reassigned is a way to inherit somebody else's entity. The conventional choices are the Entra object id, the SCIM external id, a SAML persistent NameID, an Active Directory object GUID, or a PIV FASC-N.

The value of that claim becomes the subject key, and the link it forms to an entity id is what everything downstream, roles, workspace membership, audit, actually keys off. The identity provider's notion of the subject never leaks past this boundary.

Matching policy​

The matching policy decides what happens when a subject arrives:

SettingWhat it controls
Match attributesAn ordered list, for example email then UPN then username
NormalizationExact, case-insensitive, or trimmed
Require verifiedWhether an unverified claim may be matched on at all (on by default)
Match actionauto_link, review, or reject
Create if no matchWhether an unmatched subject may be provisioned (off by default)
Dual controlWhether a link requires a second approver
Orphan policyKeep, suspend, or delete an entity whose subject disappears upstream

A review outcome denies the login and queues the subject for an operator decision, storing a hash of the subject key rather than the key itself. Approvals are single-use.

Just-in-time provisioning​

With just-in-time provisioning enabled, a first successful login mints the entity, a user for a human provider, a service_account for a workload provider, through the same identity pipeline the console uses. It passes the platform's provisioning admission gate first, so a provider cannot become an unbounded source of new principals.

Group and role mapping​

A mapping says: this group at the identity provider grants this role at this scope, and scope is one of exactly three: the organization, a node, or a workspace. Workspace-scoped mappings are how a federated person becomes a workspace member.

Two properties are worth knowing:

  • Federation only ever manages its own grants. Every grant a mapping produces is tagged with the provider that produced it, and reconciliation revokes only within that tag. A grant an administrator made by hand is never touched by a federation sync.
  • You cannot map a group onto a governance or high-privilege role. The attempt is refused rather than applied. An identity provider's group membership is not an administrative escalation path, and every grant passes the admission gate regardless.

SCIM provisioning​

System9 is a SCIM 2.0 service provider: the identity provider pushes users and groups to it. Tokens are administered from the provider's SCIM tab in the console, or over the operator API. The surface is per provider, and it is authenticated by a provider-scoped bearer token and nothing else, no session, no system token, no client certificate. A token minted for one provider cannot drive another's endpoint.

PropertyBehaviour
ResourcesUsers and Groups, with create, read, update, patch and delete
Not offeredBulk operations and password change, both advertised as unsupported
Page sizeCapped at 200 results
PasswordsDropped on write; the platform never accepts a password over SCIM
DeprovisioningRuns the standard entity lifecycle cascade, suspend, restore, soft delete
TokensStored as hashes only, with rotation lineage and a default one-year expiry. The raw value is shown once, on issue or rotation, and never again

Sync failure is handled by latching rather than by retrying forever: five consecutive failed cycles quarantine the provider's sync, and a quarantine sustained for four weeks auto-disables the provider. Re-enabling it then requires a fresh passing test, because the enable gate applies to every route back to enabled.

Assurance: requiring a strong authenticator​

Assurance is how a multi-factor requirement reaches across a federation boundary. It adds no new authenticator, it conveys and verifies the one the identity provider used.

  • Requested at login. The authentication-context values the provider binding declares are sent with the authorization request.
  • Verified at callback. The context the provider returns is classified and compared against the effective floor, which is the higher of the organization policy floor and the provider's own binding. A binding can raise the floor and can never lower it.

Only two mechanisms classify as phishing-resistant: FIDO2/WebAuthn with relying-party binding, and PKI smartcard authentication with channel binding. TOTP, SMS, push with number matching and recovery codes are multi-factor but are not phishing-resistant, and a policy that requires phishing resistance will refuse them.

Passkeys are classified further: a device-bound passkey can reach the highest assurance level, a synced passkey is capped one level below it. Both are phishing-resistant, the ceiling is about whether the key can be exported, not about whether it can be phished. The device model map used for this classification is bundled with the product, so the classification works with no network call.

At the higher federation assurance levels the platform additionally requires a hardware-backed signing key for its own service-provider identity, and refuses a software-held key.

Sessions, logout and revocation​

A federated session is minted on the same path as a local one, so it inherits cross-cell replication and the standard revocation machinery described in sessions across a node's cells. It is then stamped with the provider id and the provider's session identifier, which is what makes provider-driven logout possible.

TriggerEffect
OIDC back-channel logoutThe provider posts a signed logout token; matching sessions are revoked
SAML single logoutSame, optionally narrowed to one session index
Disabling a providerProvider-bound sessions are drained and its SCIM tokens revoked, in the same operation. The response reports how many sessions were revoked

That third row is the one that matters operationally: disabling a provider is not just "no new logins", it ends the ones already running. Without it, switching off a compromised provider would leave a standing-access window the length of a session lifetime.

A sweep ceiling worth knowing

The session sweep on provider-disable and on back-channel logout enumerates up to 500 sessions. On a node carrying more than 500 live user sessions, a single sweep can leave some behind. Where that is a concern, follow a disable with a session review rather than assuming the sweep was exhaustive.

You cannot lock yourself out​

Three guards run before any destructive transition changes state, so a refusal arrives instead of a broken estate:

GuardRefuses when
Local administrator path intactThe change would leave the node with no local administrator route
Mandated provider in useThe change would disable a provider the organization mandates
Last enabled providerAdvisory, not blocking: warns that the node returns to its local-authentication baseline

Alongside them is break-glass, a deliberately separate front door that authenticates against local credentials, never against an identity provider, and is mounted outside the federation route tree so that a wedged assertion endpoint cannot block it. It requires a specific break-glass permission at the target node, it is audited and it raises a live event when used, and no enforcement mode can remove it, including a fully organization-mandated one.

One deliberate sharp edge: break-glass must still meet the organization's assurance floor. A password-only break-glass under a floor that requires a hardware authenticator is refused and asks for step-up. There is no side door below the floor, which means the floor is a real property of the deployment rather than a default that any emergency dissolves.

Sovereign and air-gapped​

Every part of this works with no internet.

  • Local authentication is the baseline, so an air-gapped deployment that federates with nothing is a normal deployment, not a degraded one.
  • LDAP direct bind reaches a domain controller on the local network. TLS is required, and turning it off is refused unless the configuration carries an explicit air-gap acknowledgement, so "no TLS" is always a decision somebody recorded.
  • Smartcard authentication needs no identity provider at all, see below.
  • The passkey classification map is bundled, no metadata service is contacted.
  • Break-glass and the never-lockout guards are unaffected by connectivity.

There is no local identity-provider mode: System9 consumes OIDC and SAML, it does not issue them for third parties.

CAC and PIV​

Smartcard authentication comes in two shapes.

Through a federal identity provider. The card authenticates the person at an IdP that already accepts it, and System9 sees an ordinary OIDC or SAML login. This path is generally available and carries no export gate.

Direct mutual TLS. The node authenticates the card itself, with no identity provider in the path, which is the point for a disconnected or tactical deployment. Identity comes from the card's FASC-N or PIV UUID, never from a common name or email. Accepted certificate-policy object identifiers default to the federal and DoD PIV-authentication families and are configurable. Channel binding means this path reaches the highest assurance classification.

Two hard constraints:

  • Revocation checking is mandatory and fails closed. With no revocation source reachable, every smartcard login is refused. The card being valid is not enough; the platform has to be able to confirm it has not been revoked.
  • This surface is export-controlled and gated behind a military licence tier and a sovereign or tactical deployment profile.

The direct path runs through the node's gateway, which forwards the verified client certificate to the smartcard endpoint only from a trusted hop, and strips the header otherwise so it cannot be injected from outside.

Replication and audit​

Federation configuration is organization-wide state, and the replication policy says so per table: providers, governance policy, claim mappings, group mappings, SCIM tokens, assurance bindings, subject links, matching policy, invitations and reviews all converge globally and cross-node. Three tables stay local to the cell that wrote them: the SCIM sync cursor, the federation audit chain, and test results.

The audit chain is append-only and hash-chained, and it carries a hard requirement worth calling out during setup: every provider must name a service entity as its audit actor. A provider registered without one cannot write login audit entries. Registering the identity-provider service entity is a required setup step, not optional metadata.