SSO group to role mappings
Audience: TokenTimer Enterprise system administrators configuring SSO against an existing IdP.
Group-to-role mappings let you assign TokenTimer roles to users based on their IdP group membership. They live under System Settings → Single Sign-On in the dashboard and are evaluated on every SSO login.
Concepts
| Term | Meaning |
|---|---|
| Provider slug | Stable identifier for the SSO provider row (e.g. entra, okta). Each mapping is bound to one slug. |
| IdP group | The string TokenTimer matches against the user's group claims (case-insensitive). |
| Role | admin, workspace_manager, or viewer. |
| Workspace | Required when admin is not the role, but accepts NULL to make the grant global across every workspace. admin is always system-wide and rejects a workspace_id. |
| Priority | Integer in [1, 1000], default 100. Display ordering only today; reserved for future tie-breaking. Out-of-range values are rejected. |
Decision rules
- Group names are lowercased before comparison so
DevOpsanddevopsmatch the same mapping. - Highest role wins per workspace for
workspace_managerandviewermappings. The SSOadminrole is system-wide: it setsusers.is_adminand grants Workspace Manager (not workspace Admin) on every existing workspace at login. - Global non-admin mappings. A
workspace_managerorviewermapping withworkspace_id = NULL(UI: "All workspaces") grants that role across every workspace. It still loses to a more specific same-workspace mapping at higher precedence. - Admin is sticky. Removing the mapping does not demote an existing system admin. Revoke from Workspaces → Members → System admin (requires an authenticated system admin).
- Strict membership sync (opt-in; global default in System Settings → Single Sign-On, or per-provider override) revokes SSO-derived workspace memberships that are no longer mapped. Manual invitations (source=manual) are never revoked.
- System-admin expansion adds missing
workspace_manageraccess on every workspace and may raiseviewertoworkspace_managerwhere needed. It never downgrades an existing workspaceadminmembership and never revokes manual memberships.
Worked example: bind devops to admin
- Open System Settings then Single Sign-On.
- Click Add mapping.
- Fill in:
- Provider: your OIDC provider slug (e.g.
keycloak) - IdP group:
devops - Role:
Admin (system-wide) - Workspace: leave blank (admin role is global)
- Provider: your OIDC provider slug (e.g.
- Save. The next login from a
devops-group user will promote them to system admin (users.is_admin = TRUE).
They join the shared Default workspace as manager, not workspace owner, unless they are the first user on an empty install (creates Default as owner). System admin and workspace owner are documented in the Authentication model.
Worked example: workspace-scoped viewer
- Click Add mapping.
- Provider: your SAML provider slug, IdP group:
eng-london, Role:Viewer, Workspace:London. - Save. London engineers logged in via SAML now have read-only access to the London workspace.
Worked example: separate teams per workspace
| IdP group | Role | Workspace |
|---|---|---|
tt-platform-admins | admin (global) | - |
devops | workspace_manager | Infrastructure |
security-team | workspace_manager | Security |
data-eng | workspace_manager | Data |
everyone | viewer | Infrastructure |
A user in both devops and everyone ends up as workspace_manager
on Infrastructure thanks to the highest-role-wins rule.
Worked example: global viewer (every workspace)
- Add mapping.
- Provider: your OIDC provider slug, IdP group:
staff, Role:Viewer, Workspace: leave blank ("All workspaces"). - Save. Every user in the
staffgroup gets read-only access to every workspace on their next login.
The Group mappings UI defaults the workspace dropdown to the first workspace and shows a warning banner if you explicitly pick "All workspaces", because it is a powerful blast radius. The warning is informational; the mapping still saves.
Strict membership sync
Strict sync removes SSO-derived memberships when the IdP no longer
asserts the mapped group. Enable it globally under System Settings → Single Sign-On → Installation-wide SSO policy, or override per provider under Manage SSO providers → Advanced (inherit / enabled / disabled). Helm / env equivalent: SSO_STRICT_MEMBERSHIP_SYNC=true.
What gets revoked:
- Workspace memberships with
source='sso'whose workspace is not in the current resolution.
What does NOT get revoked:
- Manual invitations (
source='manual'). - System admin (sticky for safety).
Audit log: each revocation writes one row of action
MEMBERSHIP_SYNCED_FROM_SSO with metadata.change = 'revoked',
metadata.reason = 'strict_membership_sync'.
Bootstrap admin groups (IaC shortcut)
config.bootstrap_admin_groups (or config.admin_groups) on a provider row
supplies a deploy-time shortcut for the common "this AD group is admin" case,
useful for IaC pipelines that need to seed admin access before any human ever
logs in:
"config": {
"bootstrap_admin_groups": "tt-platform-admins,sre-leads"
}
They are injected as virtual admin / workspace_id=NULL mappings at resolution
time. They are not stored in the group mappings table. Anything more nuanced
(workspace-scoped, viewer/manager roles) belongs in System Settings → Single Sign-On.
Audit log actions
| Action | Emitted when |
|---|---|
SSO_GROUP_MAPPING_CREATED | Admin adds a mapping from the UI/API. |
SSO_GROUP_MAPPING_UPDATED | Admin edits a mapping. |
SSO_GROUP_MAPPING_DELETED | Admin deletes a mapping. |
MEMBERSHIP_SYNCED_FROM_SSO | A login resulted in an actual change to users.is_admin or workspace_memberships. One row per change. Idempotent: a login that resolves to the same grants as last time does not emit rows. |
LOGIN_SUCCESS (metadata) | idp_groups_seen, idp_groups_sample (up to 20 group values), matched_groups (array), matched_groups_count, admin_granted, workspace_grants_count, workspace_revocations_count. |
Troubleshooting
Users in my admin group aren't getting admin.
Check the LOGIN_SUCCESS audit row. idp_groups_seen shows how many groups
the IdP emitted; idp_groups_sample lists up to 20 raw values TokenTimer
parsed from the assertion. matched_groups shows how many matched a mapping.
If idp_groups_seen is 0, your IdP isn't emitting groups, or the claim
path is wrong. Check Claim mapping.
For Entra SAML, group claims often carry object IDs (GUIDs), not
display names. Map the exact string from idp_groups_sample, and sign out
and back in after editing a mapping.
For Keycloak, groups are not in the token until you add a Group Membership mapper on the OIDC client and assign users to groups in the realm. Follow Option B in the Keycloak guide (sections 4 and 9), then log out and sign in again. Quick SQL check:
SELECT metadata->>'idp_groups_seen', metadata->'matched_groups'
FROM audit_events
WHERE action = 'LOGIN_SUCCESS' AND metadata->>'method' = 'oidc'
ORDER BY id DESC LIMIT 1;
Also confirm the mapping uses System Admin (global) (workspace_id NULL) for
system-wide admin, not workspace-scoped manager/viewer roles only.
Strict-sync removed a user's manual workspace membership.
Strict-sync only touches rows where source='sso'. If a manual row was
removed, check the audit log for MEMBERSHIP_REMOVED from a human admin.
The schema guarantees manual rows are skipped.
Admin role is gone after disabling a mapping.
It shouldn't be. Admin is sticky on this code path. If a user lost admin
through SSO sync, the audit row MEMBERSHIP_SYNCED_FROM_SSO will show
change = 'admin_revoked' (impossible by design; please file a bug).