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
| Protocol | Best for | Provisioning |
|---|---|---|
| SAML 2.0 | Mature enterprise IdPs, rich assertion model | JIT or SCIM |
| OIDC | Modern providers, contractor or partner SSO | JIT or SCIM |
| LDAP / AD | On-prem directories, air-gapped sites | Polling sync |
| SCIM 2.0 | Push-based lifecycle, fast deprovisioning | Standalone 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: 5mBecause 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: trueWhen 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-samlMultiple 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 3For 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
- Roles & permissions — mapping IdP groups to Wardengate roles.
- Organizations — per-org IdP configuration and tenant isolation.
- MFA configuration — IdP-chained MFA vs native Wardengate MFA.