Wardengate

Administer

Identity providers

Wardengate never owns users. Every human principal flows in from an identity provider, which authenticates the sign-in and supplies the attributes Wardengate uses to evaluate policy. This page covers the four connection protocols and how to pick between them.

Protocols at a glance

ProtocolBest forProvisioning
SAML 2.0Mature enterprise IdPs, rich assertion modelJIT or SCIM
OIDCModern providers, contractor or partner SSOJIT or SCIM
LDAP / ADOn-prem directories, air-gapped sitesPolling sync
SCIM 2.0Push-based lifecycle, fast deprovisioningStandalone or paired with SAML/OIDC

Attribute mapping

Every provider boils down to four values Wardengate needs:

  • email — the canonical identifier. Must be immutable in your IdP.
  • externalId — a stable opaque identifier used for lookup and reconciliation. Prefer the IdP's internal ID over email, so a rename does not create a new Wardengate user.
  • displayName — shown in the UI and in audit logs.
  • groups — the list of group names or IDs that policy evaluation will look at. This is the one that trips most first-time configs.

SAML 2.0

SAML is the path of least resistance for most enterprise IdPs. Point Wardengate at your metadata URL and it self-configures the entity ID, certificates, and endpoints. If your IdP cannot reach the control plane for metadata fetch, upload the XML as a file instead.

apiVersion: wardengate/v1
kind: IdentityProvider
metadata:
  name: corp-saml
  organization: acme
spec:
  protocol: saml
  metadataUrl: https://idp.acme.example/saml/wardengate/metadata
  signedRequests: true
  signedAssertionsRequired: true
  nameIdFormat: emailAddress
  attributeMapping:
    email: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
    externalId: http://schemas.microsoft.com/identity/claims/objectidentifier
    displayName: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
    groups: http://schemas.microsoft.com/ws/2008/06/identity/claims/groups
  allowedDomains: ["acme.example"]
  jit:
    createUsers: true
    defaultRoles: ["user"]

Two footguns worth calling out: some IdPs emit group GUIDs rather than names, and the default SAML response lifetime is shorter than you think. If assertions look fine in a browser debugger but log-in fails, check the control plane clock skew first.

OpenID Connect

OIDC is the better choice when you want short-lived refresh tokens, PKCE-protected authorization code flows, or a cleaner path for contractors who live in a separate tenant. Register Wardengate as a confidential client in your IdP, with the redirect URI set to the base URL you configured earlier plus /auth/oidc/callback/<name>.

apiVersion: wardengate/v1
kind: IdentityProvider
metadata:
  name: partner-oidc
  organization: acme
spec:
  protocol: oidc
  issuer: https://auth.partner.example/
  clientId: wardengate-prod
  clientSecretRef: partner-oidc-secret
  scopes: ["openid", "email", "profile", "groups"]
  attributeMapping:
    email: email
    externalId: sub
    displayName: name
    groups: groups
  pkce: true
  jit:
    createUsers: true
    defaultRoles: ["contractor"]

The groups claim is not part of the OIDC core spec. If your provider does not emit it by default, add a custom claim mapping on the IdP side that turns directory group membership into a JSON array.

LDAP and Active Directory

LDAP is the fallback for environments where a browser SSO round trip is impractical — on-prem OT networks, lab sites without a corporate IdP, legacy AD-only shops. Wardengate binds with a read-only service account, runs a search every five minutes by default, and walks nested groups up to a configurable depth.

apiVersion: wardengate/v1
kind: IdentityProvider
metadata:
  name: site-ad
  organization: acme
spec:
  protocol: ldap
  url: ldaps://dc01.internal.acme.example:636
  caBundleRef: internal-ca-bundle
  bindDN: "CN=svc-wardengate,OU=Service Accounts,DC=acme,DC=example"
  bindPasswordRef: site-ad-bind-secret
  userBaseDN: "OU=People,DC=acme,DC=example"
  userFilter: "(&(objectClass=user)(mail=*))"
  groupBaseDN: "OU=Groups,DC=acme,DC=example"
  groupFilter: "(objectClass=group)"
  nestedGroupDepth: 5
  attributeMapping:
    email: mail
    externalId: objectGUID
    displayName: displayName
    groups: memberOf
  syncInterval: 5m

Because LDAP bind is synchronous per login, the gateway tolerates a short directory outage by caching the last successful auth for up to the configured offlineGrace. That is a trade-off: longer grace means better availability, weaker deprovisioning. Keep it under an hour.

SCIM 2.0 provisioning

SCIM is how your IdP pushes user and group lifecycle to Wardengate instead of waiting for a sign-in. It is the only way to deprovision sessions the moment a user is disabled in the IdP, rather than when their token expires. Pair SCIM with either SAML or OIDC — use SAML or OIDC to authenticate, SCIM to own the directory.

apiVersion: wardengate/v1
kind: IdentityProvider
metadata:
  name: corp-scim
  organization: acme
spec:
  protocol: scim
  pairedProvider: corp-saml
  endpoint: https://wardengate.example.com/api/v1/scim/v2
  tokenRef: corp-scim-bearer-token
  schemas:
    userExtensions:
      - urn:ietf:params:scim:schemas:extension:enterprise:2.0:User
  groupSync:
    filter: 'displayName sw "wardengate-"'
  revokeOnDisable: true

When a SCIM PATCH sets active=false on a user, the control plane cancels any in-flight sessions within seconds. That is the behaviour you want when the help-desk disables a departing employee — the user is not just unable to start a new session, their existing shells are cut.

JIT vs SCIM provisioning

Just-in-time creates the Wardengate user record on first successful sign-in; SCIM creates it as soon as the IdP assigns the application. The difference matters for two reasons: policy evaluation and auditing.

  • JIT users cannot be granted scoped roles before their first login, because the user does not exist yet. If you need to pre-stage access for a named human, use SCIM.
  • Audit reports that ask "who had access yesterday" need the user record to exist, not just the IdP mapping. SCIM fills the history; JIT leaves gaps for dormant accounts.
  • Deprovisioning is fast under SCIM, best-effort under JIT. Under JIT, a disabled user is only really gone once their existing Wardengate session token expires.

Group sync semantics

Wardengate stores groups by their canonical name from the provider and never rewrites them. Group assignment is re-evaluated at every login for SAML and OIDC, continuously for SCIM, and on the polling interval for LDAP. Policies reference groups by name — if the IdP renames a group, update the policy or add an alias:

wgctl group alias create \
  --from "platform-eng" \
  --to "sre" \
  --idp corp-saml

Multiple providers in one org

It is fine — encouraged, even — to register more than one IdP in the same organization. Common patterns are a primary IdP for staff and a secondary for vendors or contractors with a different policy footprint, or a break-glass OIDC connection to a separate tenant used only if the primary IdP is down.

Testing and debugging

Every provider has a dry-run mode in the console that walks an assertion through the attribute mapping and shows what user and groups would result, without actually creating anything. Use it whenever you change a claim, because the difference between groups and Groups will cost you an hour.

wgctl idp test corp-saml \
  --assertion-file ./sample-assertion.xml

# expected
# resolved.email         jane.doe@acme.example
# resolved.externalId    8f6b1c...
# resolved.displayName   Jane Doe
# resolved.groups        [wardengate-admins, sre]
# jit.wouldCreate        false (user exists)
# policy.matches         3

For live debugging, open the IdP's log stream in the admin console. Every inbound assertion, token introspection, and SCIM call is recorded with its raw payload — redacted to strip the signature — for 48 hours.

Related