Skip to main content
logoTetrate Service BridgeVersion: 1.14.x

OIDC Just-in-Time User Provisioning

TSB can automatically create user accounts and reconcile team memberships the first time a user authenticates via OIDC, without requiring a separate teamsync process. This is called Just-in-Time (JIT) provisioning.

JIT provisioning is useful when:

  • Your identity provider is not supported by teamsync, or teamsync is not available in your environment.
  • You want users to be created automatically on first login rather than pre-provisioning them.
  • You need team membership to reflect OIDC group claims without running a periodic sync job.

How it works

When a user authenticates for the first time, TSB:

  1. Extracts the user's identity from the configured claim in the OIDC token (by default, the sub claim).
  2. Creates a TSB user account if one does not already exist. The user is marked with sourceType: OIDC_PROVISIONED.
  3. If group synchronization is enabled, reads the user's group memberships and updates any TSB teams that have mapped those groups via oidcGroups.

On subsequent logins, TSB re-evaluates group memberships and reconciles any changes within the configured drift window.

There is no background sync job

Group-to-team reconciliation is not a periodic process. It runs synchronously as part of OIDC login and token refresh — a user's memberships are only re-evaluated when they authenticate. maxDriftWindow (see Drift bounding) only bounds how stale a still-logged-in session's memberships can get before the next login or refresh re-resolves them; it does not cause TSB to poll or re-check memberships on its own.

JIT provisioning does not modify existing users

JIT provisioning only creates a TSB user the first time it sees a subject that does not already exist. If a user record with the same identity is already present — for example, a user previously synced from LDAP or created manually — TSB authenticates the OIDC login against that existing record and leaves its fields, including sourceType, unchanged. JIT does not re-stamp or migrate an existing user's sourceType to OIDC_PROVISIONED, and there is no built-in flow to convert an existing user to OIDC.

Group synchronization is unaffected by this: it reconciles team memberships for the authenticated user regardless of how the user record was originally created.

Prerequisites

Before you begin:

✓ TSB is configured with an OIDC identity provider
✓ You have access to edit the ManagementPlane CR or Helm values
✓ Your OIDC provider issues tokens with a claim listing the user's group memberships (typically groups)

Enabling JIT provisioning

Add the jitProvisioning block to your identityProvider.oidc configuration in the ManagementPlane CR or Helm values:

spec:
identityProvider:
oidc:
clientId: <client-id>
redirectUri: https://<tsb-address>:8443/iam/v2/oidc/callback
scopes:
- email
- profile
# Add the jitProvisioning block:
jitProvisioning:
enabled: true
providerName: my-oidc-provider
subjectClaim: sub
groupsClaim: groups
FieldDescriptionDefault
enabledEnables JIT user provisioning.false
providerNameAn identifier for this OIDC provider. Used to associate JIT-created users with their provider. Must be unique across providers.Required
subjectClaimThe JWT claim to use as the user's unique identifier.sub
groupsClaimThe JWT claim that lists the user's group names. Used for group synchronization.groups
groupSyncConfiguration for group synchronization. See Group synchronization.
Helm installation

This configuration can also be applied to Helm values files under spec.identityProvider.oidc.jitProvisioning.

Group synchronization

Group synchronization maps OIDC group names from the token to TSB team memberships. It is configured via the groupSync block within jitProvisioning.

jitProvisioning:
enabled: true
providerName: my-oidc-provider
groupsClaim: groups
groupSync:
mode: MEMBERSHIP_ONLY
maxDriftWindow: 1h
requireMappedTeam: false

Synchronization modes

ModeBehavior
OFFNo group synchronization. Users are created on first login but team memberships are not managed by JIT.
DRY_RUNGroup reconciliation is evaluated and logged, but no team membership changes are applied. Use this to validate your team mappings before enabling live sync.
MEMBERSHIP_ONLYTeam memberships are actively reconciled on each login based on the OIDC group claims in the token.

In MEMBERSHIP_ONLY mode, reconciliation is authoritative: on every login TSB adds the user to every team whose matching rule is satisfied by the token's group claims, and removes them from any team it previously added them to (via matching) that the claims no longer satisfy — subject to maxDriftWindow. Teams with excludeFromJitReconcile: true (see Excluding a team from JIT reconciliation) are never added to or removed from.

Viewing reconciliation logs

Group synchronization runs in the Management Plane's iam pod, not the tsb pod. To see the group values TSB extracted from the token and which teams they matched (or failed to match), tail the IAM service logs:

kubectl logs deployment/iam -n tsb

This is the fastest way to confirm what your identity provider is actually sending — in DRY_RUN mode in particular, the IAM logs are the only place the previewed add/remove decisions and any unmatched group values are visible, since no team membership changes are applied.

Drift bounding

The maxDriftWindow field controls how long TSB waits before removing a user from a team when the group claim disappears from the OIDC token. This prevents transient IdP issues or token caching from immediately revoking team memberships.

The value is a duration string (for example, 15m, 1h, 4h). Accepted range is 15 minutes to 24 hours.

groupSync:
mode: MEMBERSHIP_ONLY
maxDriftWindow: 1h

If a user's OIDC token no longer contains a group that previously granted team membership, TSB will not remove the user from the team until maxDriftWindow has elapsed without the group appearing.

Requiring mapped team membership

Set requireMappedTeam: true to prevent login for users who do not belong to at least one TSB team that has a matching oidcGroups mapping:

groupSync:
mode: MEMBERSHIP_ONLY
requireMappedTeam: true

This is useful for restricting access to only users who have been explicitly mapped to a TSB team.

Mapping OIDC groups to TSB teams

A group value from the token matches a TSB team in one of two ways:

  • The team's name is exactly equal to the group value, or
  • The team lists the group value in its oidcGroups field.

Matching is exact and case-sensitive in both cases. To associate an OIDC group with a TSB team explicitly, add the group names to the team's oidcGroups field:

apiVersion: api.tsb.tetrate.io/v2
kind: Team
metadata:
name: platform-engineers
organization: myorg
spec:
displayName: Platform Engineers
oidcGroups:
- platform-eng # OIDC group name as it appears in the token claim
- platform-engineering # Multiple group names can map to the same team

When a user logs in and their token contains any of the listed group names, TSB adds them to the platform-engineers team. If the groups disappear from the token (and maxDriftWindow has elapsed), TSB removes the membership.

Teams are never created automatically

JIT provisioning only reconciles membership in TSB teams that already exist. A group value in the token that doesn't match any team's name or oidcGroups is simply ignored — TSB never creates a team on the fly. Create the target teams ahead of time (via tctl, the API, or GitOps) and tag them with oidcGroups before your users log in. Use DRY_RUN mode to see which group values from a real login didn't match any team.

AD/LDAP-backed providers often send a distinguished name, not a short group name

With AD-backed OIDC providers, the group claim's value is often the group's full distinguished name or CN — for example CN=platform-engineers,OU=Groups,DC=corp,DC=example,DC=com — rather than a short group name. Whatever the literal string in the claim is, that exact string (case included) is what must appear as the team's name or in its oidcGroups. Check the IAM logs to see the value TSB actually received before configuring a team.

Excluding a team from JIT reconciliation

To prevent JIT provisioning from modifying a particular team's membership — for example, a team managed by another system — set excludeFromJitReconcile: true:

spec:
displayName: Special Access Team
excludeFromJitReconcile: true

JIT provisioning will never add or remove members from teams with this flag set, even if oidcGroups mappings are also configured.

Using Google Admin SDK for group discovery

When OIDC tokens do not include group membership claims, TSB can query the Google Admin SDK directly to retrieve group memberships for each user. This requires a Google Workspace service account with domain-wide delegation.

Create a service account

  1. In the Google Cloud Console, create a service account with no roles.
  2. Generate a JSON key for the service account.
  3. In Google Workspace Admin, grant the service account domain-wide delegation with the scope https://www.googleapis.com/auth/admin.directory.group.readonly.

Create the Kubernetes secret

Store the service account key JSON in a Kubernetes secret in the tsb namespace:

kubectl create secret generic google-admin-sa \
--from-file=credentials.json=./service-account-key.json \
-n tsb

Configure the ManagementPlane CR

Reference the secret in the groupSync.googleAdminSdk block:

jitProvisioning:
enabled: true
providerName: my-oidc-provider
subjectClaim: sub
groupSync:
mode: MEMBERSHIP_ONLY
maxDriftWindow: 1h
googleAdminSdk:
impersonateSubject: admin@example.com # A Google Workspace admin account to impersonate
serviceAccountSecretRef: google-admin-sa
scopes:
- https://www.googleapis.com/auth/admin.directory.group.readonly
FieldDescription
impersonateSubjectEmail address of a Google Workspace admin to impersonate when calling the Admin SDK.
serviceAccountSecretRefName of the Kubernetes secret (in the tsb namespace) containing the service account JSON key.
scopesOAuth scopes requested for the Admin SDK. At minimum, include admin.directory.group.readonly.

When googleAdminSdk is configured, TSB uses the Admin SDK to look up the full list of Google groups for the authenticated user's email, rather than reading group claims from the OIDC token. The groupsClaim field is ignored when a groupSource is configured.

Migrating from teamsync

If you are currently using teamsync to synchronize users and teams, you can migrate to JIT provisioning using the following approach:

  1. Enable JIT provisioning with groupSync.mode: DRY_RUN. This logs what changes would be made without applying them.
  2. Review the IAM service logs to verify that group-to-team mappings are correct.
  3. Add oidcGroups mappings to your teams.
  4. Switch groupSync.mode to MEMBERSHIP_ONLY once you are satisfied with the dry-run output.
  5. Disable teamsync after confirming that JIT provisioning is managing memberships correctly.
caution

JIT provisioning only manages users who have logged in via OIDC since provisioning was enabled. Users who were previously synced by teamsync but have never logged in directly will not be processed by JIT provisioning until their next login.

Disabling the built-in admin account

Once OIDC JIT provisioning is in use, you can harden the management plane by disabling the built-in local admin account so that only OIDC users and TSB service accounts can log in.

Set disableLocalCredentials: true on spec.identityProvider in the ManagementPlane CR:

spec:
identityProvider:
oidc:
clientId: <client-id>
redirectUri: https://<tsb-address>:8443/iam/v2/oidc/callback
# ...
disableLocalCredentials: true

When this field is true, the operator stops mounting the local credentials volume into the IAM server. The built-in admin account and any other local credentials can no longer authenticate. TSB service accounts are unaffected because they use JWT bearer tokens independently of the local credential provider.

note

A webhook guardrail prevents setting disableLocalCredentials: true unless an external identity provider (OIDC or LDAP) is already configured, so it is not possible to lock all interactive users out of the management plane.

To re-enable the admin account for troubleshooting, set disableLocalCredentials: false and let the operator reconcile. The admin-credentials Secret is preserved when the account is disabled, so no password regeneration is required.

Known limitations

  • No deprovisioning on IdP removal: If a user is removed from the identity provider without ever logging in again, their TSB account and team memberships are not automatically revoked. Memberships are only reconciled on login.
  • Single OIDC provider: JIT provisioning is scoped to one OIDC provider configuration. If multiple OIDC providers are in use, each provider requires its own providerName.
  • Group claims size limits: Some OIDC providers cap the number of groups included in a token. If a user belongs to more than the provider's limit, groups may be missing from the token. Use the Google Admin SDK group source to work around this for Google Workspace.