Identity & Users
IdP configuration, user provisioning, and role management
Admin Guide: Identity and User Management
This guide is for platform administrators who manage identity providers, user provisioning, roles, groups, and sessions in the Admin Console.
The platform uses a hybrid identity model: authentication is always delegated to an external Identity Provider (IdP), while the platform maintains local user records for role assignment, entitlements, activity tracking, and cost attribution.
Where to manage: Admin Console > Tenant Manager (
/admin/tenants) > Identity tab, and Admin Console > User Directory (/admin/users).
Table of Contents
- Overview: Hybrid IdP Model
- Tenant IdP Configuration
- OIDC Settings
- Claim Mappings
- User Provisioning Modes
- Role Management
- Group Management
- Session Management
- User Directory Operations
- SCIM 2.0 Integration
- Troubleshooting IdP Issues
Overview: Hybrid IdP Model
The platform never stores or manages user credentials directly. Instead, each tenant configures one or more external IdPs to handle authentication:
User visits platform
|
v
Redirected to tenant's IdP (Keycloak, Azure AD, or OIDC provider)
|
v
User authenticates at IdP
|
v
Redirected back with OIDC authorization code
|
v
Platform exchanges code for ID token + access token
|
v
Platform extracts claims: sub, groups, roles, email, name
|
v
Platform looks up PlatformUser by sub + tenantId
|
+-- Found: update lastLogin, refresh groups (JIT), return session
+-- Not found: create PlatformUser, assign default role, return session
The IdP is the source of truth for credentials and group memberships. The platform is the source of truth for platform-specific role assignments, agent entitlements, and activity/cost tracking.
Tenant IdP Configuration
Each tenant configures its own IdP. Navigate to Admin Console > Tenant Manager > [Tenant] > Identity tab.
Supported Providers
| Provider | Value | Best For |
|---|---|---|
| Keycloak | keycloak | EU data-sovereign tenants, self-hosted identity |
| Azure AD / Entra ID | azure-ad | Microsoft ecosystem enterprises |
| Generic OIDC | oidc-generic | Any OIDC-compliant provider (Okta, Auth0, Ping, etc.) |
TenantIdpConfig Schema
interface TenantIdpConfig {
tenantId: string;
provider: 'keycloak' | 'azure-ad' | 'oidc-generic';
provisioningMode: 'scim' | 'jit' | 'bundled-keycloak';
oidcConfig: {
issuerUrl: string;
clientId: string;
// clientSecret stored in Vault, not in this config
scopes: string[];
claimMappings: {
groups: string; // JWT claim path for groups
roles: string; // JWT claim path for roles
tenantId: string; // JWT claim path for tenant ID
};
};
roleMappingStrategy: 'idp-groups' | 'idp-roles' | 'manual';
roleMappings?: { group: string; role: 'consumer' | 'builder' | 'admin' }[];
sessionConfig: {
maxDurationMinutes: number;
concurrentSessionLimit?: number;
};
scimEndpoint?: string; // Only if provisioningMode == 'scim'
}
Configuration Steps
- Select provider: Choose Keycloak, Azure AD, or Generic OIDC.
- Enter OIDC settings: Issuer URL, Client ID, Scopes.
- Store client secret: The client secret is stored in Vault. Enter it via the secure input field (it will be masked after saving).
- Configure claim mappings: Map JWT claims to platform fields (groups, roles, tenant ID).
- Select provisioning mode: SCIM 2.0, JIT, or Bundled Keycloak.
- Set up role mappings: Map IdP groups to platform roles.
- Configure session policy: Set max duration and concurrent session limits.
- Test the connection: Click "Test IdP Connection" to perform OIDC discovery and validate the configuration.
OIDC Settings
Required Fields
| Field | Description | Example |
|---|---|---|
| Issuer URL | The OIDC issuer URL. Must support /.well-known/openid-configuration. | https://login.microsoftonline.com/{tenant-id}/v2.0 |
| Client ID | The OAuth 2.0 client ID registered in the IdP. | a1b2c3d4-e5f6-7890-abcd-ef1234567890 |
| Scopes | OIDC scopes to request. Must include openid. | ["openid", "profile", "email", "groups"] |
Provider-Specific Defaults
Keycloak:
{
"issuerUrl": "https://keycloak.example.com/realms/my-realm",
"scopes": ["openid", "profile", "email", "groups"],
"claimMappings": {
"groups": "groups",
"roles": "realm_access.roles",
"tenantId": "tenant_id"
}
}
Azure AD / Entra ID:
{
"issuerUrl": "https://login.microsoftonline.com/{azure-tenant-id}/v2.0",
"scopes": ["openid", "profile", "email", "GroupMember.Read.All"],
"claimMappings": {
"groups": "groups",
"roles": "roles",
"tenantId": "tid"
}
}
Generic OIDC:
{
"issuerUrl": "https://your-provider.example.com",
"scopes": ["openid", "profile", "email"],
"claimMappings": {
"groups": "custom:groups",
"roles": "custom:roles",
"tenantId": "custom:tenant"
}
}
Claim Mappings
Claim mappings tell the platform which JWT claims contain the user's groups, roles, and tenant identifier. These are configured per tenant because different IdPs use different claim names.
Configuration
claimMappings: {
groups: string; // e.g. 'groups' or 'cognito:groups'
roles: string; // e.g. 'roles' or 'realm_access.roles'
tenantId: string; // e.g. 'tenant_id' or 'tid' or 'custom:tenant'
}
Common Claim Paths by Provider
| Provider | Groups Claim | Roles Claim | Tenant Claim |
|---|---|---|---|
| Keycloak | groups | realm_access.roles | tenant_id (custom) |
| Azure AD | groups (GUIDs) or securityGroups | roles (App Roles) | tid |
| Okta | groups | roles | organization |
| Auth0 | https://myapp/groups (custom namespace) | https://myapp/roles | org_id |
Testing Claim Mappings
Use the "Test mapping" button in the Identity tab. It accepts a sample JWT payload and shows what platform role and groups would be extracted:
Sample JWT input: { "sub": "user123", "groups": ["eng-team"], "roles": ["developer"], "tid": "tenant-1" }
Result:
- Platform groups: ["eng-team"]
- Platform role mapping: "eng-team" -> consumer (from role mappings)
- Tenant ID: "tenant-1"
User Provisioning Modes
The provisioning mode determines how user records are created and kept current in the platform.
SCIM 2.0 Sync (Recommended)
Best for: Production deployments where builders need to browse real IdP groups when configuring agent entitlements.
The IdP pushes user and group changes to the platform in real time via SCIM 2.0 endpoints.
How it works:
- Configure the IdP to push to the platform's SCIM endpoint:
POST /api/scim/v2/Users,PATCH /api/scim/v2/Users/:id,DELETE /api/scim/v2/Users/:id,GET /api/scim/v2/Groups. - The platform maintains a synchronized, browsable cache of all users and groups.
- When a user is deprovisioned in the IdP, the SCIM DELETE deprovisions them in the platform immediately.
- Group membership changes propagate without requiring user re-login.
Advantages:
- Agent entitlement group pickers show real, up-to-date IdP groups.
- User deprovisioning is immediate (no waiting for next login).
- Group changes take effect without requiring affected users to re-authenticate.
Configuration:
{
"provisioningMode": "scim",
"scimEndpoint": "https://platform.example.com/api/scim/v2"
}
JIT Provisioning (Just-In-Time)
Best for: Simpler deployments where SCIM is not available.
User records are created on first login from JWT claims. Groups are refreshed on each subsequent login.
How it works:
- User logs in via OIDC.
- Platform extracts
sub,email,name,groups,rolesfrom the JWT. - If no
PlatformUserrecord exists for thissub+tenantId, one is created. - If a record exists,
lastLoginandgroupsare updated from the JWT.
Limitations:
- Groups only update when users log in. If a user is removed from a group in the IdP, they retain access until their next login.
- The group picker in Agent Designer only shows groups that have been seen via user logins (not a complete list).
- User deprovisioning requires explicit admin action in the platform (or the user's next login attempt fails at the IdP).
Bundled Keycloak
Best for: Standalone or proof-of-concept deployments where no external IdP exists.
The platform ships its own Keycloak instance for identity management.
How it works:
- The platform deploys a Keycloak instance as part of its infrastructure.
- Users, groups, and credentials are managed directly in Keycloak.
- The platform connects to this Keycloak instance via OIDC, same as any external IdP.
Configuration:
{
"provisioningMode": "bundled-keycloak",
"provider": "keycloak",
"oidcConfig": {
"issuerUrl": "https://keycloak.platform-internal.svc/realms/platform"
}
}
Role Management
Platform Roles
The platform has three roles, each granting access to specific applications:
| Role | Applications | Capabilities |
|---|---|---|
consumer | Portal only | Chat with agents, approve/reject HITL requests, view history |
builder | Portal + Studio | Create agents, configure access control, deploy, test, monitor |
admin | Portal + Studio + Admin Console | Manage tenants, users, policies, platform health, compliance |
Role Assignment Strategies
Configure via roleMappingStrategy in the tenant IdP config:
Strategy: idp-groups -- Map IdP groups to platform roles.
{
"roleMappingStrategy": "idp-groups",
"roleMappings": [
{ "group": "agentic-admins", "role": "admin" },
{ "group": "agentic-builders", "role": "builder" },
{ "group": "all-staff", "role": "consumer" }
]
}
Evaluation order: The most privileged matching role wins. If a user is in both agentic-builders and all-staff, they receive the builder role.
Strategy: idp-roles -- Map IdP role claims directly to platform roles.
The platform reads the roles claim from the JWT and maps matching values. Useful when the IdP natively supports application roles (e.g., Azure AD App Roles).
Strategy: manual -- Roles are assigned by an admin in the User Directory.
No automatic mapping from IdP. Every user starts with the default role (configurable, typically consumer). Admins manually assign builder or admin roles via Admin Console > User Directory.
Changing a User's Role
- Navigate to Admin Console > User Directory (
/admin/users). - Click on the user.
- In the user detail panel, change the "Platform Role" dropdown.
- Save. The change takes effect on the user's next API request (session is refreshed).
The role change source is recorded in the audit trail: "Role changed from consumer to builder by admin-user-1 on 2025-06-15."
Group Management
Where Groups Come From
Groups originate in the IdP and are synchronized to the platform. The platform does not create or manage groups; it reads them from the IdP.
| Source | Sync Mechanism | Freshness |
|---|---|---|
| SCIM 2.0 | IdP pushes changes | Real-time |
| JIT | Extracted from JWT on login | Updated per login |
| Bundled Keycloak | Managed in Keycloak admin console | Immediate (same infrastructure) |
Default Groups
The platform ships with a default set of groups for mock/development mode:
| Group | Description |
|---|---|
engineering | Engineering and development team |
hr-team | Human resources team |
finance | Finance and accounting team |
executives | C-suite and executive leadership |
all-staff | All employees (default group) |
In production, groups are populated from the IdP and will reflect the actual organizational structure.
Groups and Agent Entitlements
Groups are the primary mechanism for Dimension 1 (User -> Agent) access control. When a builder configures an agent with allowedGroups: ["hr-team"], only users whose IdP group membership includes hr-team can see and use the agent.
The group picker in Agent Studio's Access tab is populated from the GET /api/groups endpoint, which returns IdpGroupCache entries:
interface IdpGroupCache {
tenantId: string;
groupName: string;
memberCount: number;
lastSynced: string;
}
For SCIM provisioning, this cache is always up to date. For JIT provisioning, it contains only groups seen in user logins.
Viewing Group Memberships
Admin Console > User Directory > select a user > "Group Memberships" section shows:
- List of IdP groups the user belongs to
- Last synced timestamp per group
- Source indicator (SCIM-synced or JIT-cached)
Session Management
Session Configuration
Sessions are configured per tenant in the IdP config:
sessionConfig: {
maxDurationMinutes: number; // Default: 480 (8 hours)
concurrentSessionLimit?: number; // Optional: max active sessions per user
}
Recommended Session Durations by Role
| Role | Recommended Max Duration | Rationale |
|---|---|---|
consumer | 480 minutes (8 hours) | Standard workday access |
builder | 240 minutes (4 hours) | Elevated privileges, shorter sessions |
admin | 60 minutes (1 hour) | Highest privileges, strict session limits |
Session Lifecycle
- Creation: Session starts when user completes OIDC login. Contains:
userId,role,tenantId,groups,permissions,idpSource. - Heartbeat: Session validity is checked every 60 seconds on active clients.
- Expiry: Session expires after
maxDurationMinutesfrom creation. User is redirected to the login page with a "Session expired" message. - Forced Logout: Admins can terminate all sessions for a user via User Directory > "Sign out all sessions."
- IdP Logout: On user logout, the platform clears the local session and redirects to the IdP's logout endpoint for single sign-out.
Concurrent Session Limits
If concurrentSessionLimit is set, the platform enforces a maximum number of active sessions per user. When the limit is reached, the oldest session is terminated.
Viewing Active Sessions
Users can view their own sessions at Account Settings (/settings) > "Active Sessions" section. Each session shows:
- Device/browser information
- IP address
- Last activity timestamp
- "Current session" indicator
Users can click "Sign out all other sessions" to terminate all sessions except the current one.
Admins can view sessions for any user via User Directory > user detail > "Activity" section.
User Directory Operations
The User Directory at /admin/users provides a comprehensive view of all provisioned users.
User Table Columns
| Column | Description |
|---|---|
| Display Name | User's full name from IdP |
| Email address from IdP | |
| IdP Source | Badge: Keycloak / Azure AD / OIDC |
| Platform Role | Badge: consumer / builder / admin |
| Groups | Chip list of IdP groups |
| Provisioning | SCIM / JIT / Local |
| Status | active / suspended / deprovisioned |
| Last Login | Timestamp of most recent login |
Available Actions
| Action | Description | API Endpoint |
|---|---|---|
| Change role | Change a user's platform role | PUT /api/admin/users/:id/role |
| Suspend | Block the user from accessing the platform | POST /api/admin/users/:id/suspend |
| Reactivate | Restore access for a suspended user | POST /api/admin/users/:id/reactivate |
| View entitlements | See which agents this user can access (Dim 1 reverse lookup) | GET /api/admin/users/:id/agents |
| Open in IdP | Deep link to the IdP admin console for this user | External link |
Important: Suspending a user in the platform does not modify their IdP account. It only blocks platform access. To fully deprovision a user, deactivate them in the IdP as well.
Reverse Lookups
From a user record, you can answer:
- "Which agents can this user access?" -- Lists all agents where the user matches entitlements (Dimension 1).
- "What role does this user have?" -- Shows the current platform role and its source (mapped from IdP group or manually assigned).
From an agent record (in Agent Registry), you can answer:
- "Who can access this agent?" -- Lists all users whose group/role/ID matches the agent's entitlements.
SCIM 2.0 Integration
Platform SCIM Endpoints
The platform exposes the following SCIM 2.0 endpoints for IdP provisioning:
| Operation | Endpoint | Description |
|---|---|---|
| Create user | POST /api/scim/v2/Users | Called when a user is created in the IdP |
| Update user | PATCH /api/scim/v2/Users/:id | Called when user attributes change |
| Deprovision user | DELETE /api/scim/v2/Users/:id | Called when a user is removed from the IdP |
| List groups | GET /api/scim/v2/Groups | Called by IdP to sync group information |
Configuring SCIM in the IdP
Keycloak:
- Install the SCIM provisioning plugin for Keycloak.
- Configure the SCIM client to point at the platform's SCIM endpoint.
- Set the Bearer token for authentication (generated in the platform's tenant configuration).
Azure AD / Entra ID:
- In the Azure portal, navigate to your Enterprise Application.
- Go to Provisioning > Automatic provisioning.
- Set the Tenant URL to
https://platform.example.com/api/scim/v2. - Set the Secret Token to the SCIM bearer token from the platform.
- Test the connection and start provisioning.
SCIM Status Monitoring
In the Tenant Manager > Identity tab, the SCIM section shows:
- Last sync timestamp: When the last SCIM event was received.
- Sync status: Active / Error / Stale (no events in 24 hours).
- Error log: Recent SCIM errors (authentication failures, schema mismatches, etc.).
Troubleshooting IdP Issues
"Session Expired" on Login
Cause: The session maxDurationMinutes has been reached.
Resolution: This is expected behavior. The user must re-authenticate. If the duration is too short, increase maxDurationMinutes in the tenant's session configuration.
"Account Suspended" on Login
Cause: An admin has suspended the user in the platform.
Resolution: Navigate to User Directory, find the user, and click "Reactivate" if appropriate. Check the audit log for who suspended the account and why.
"IdP Unreachable" on Login
Cause: The platform cannot reach the configured IdP's OIDC discovery endpoint.
Resolution:
- Verify the Issuer URL is correct and accessible from the platform's network.
- Check network connectivity (DNS, firewalls, proxy settings).
- Verify the IdP service is running.
- Use "Test IdP Connection" in the Identity tab to diagnose.
Users Not Seeing Updated Groups
Cause (JIT mode): Groups are only refreshed on login. The user has not logged in since the group change.
Resolution: Ask the user to log out and log back in. For real-time group updates, switch to SCIM 2.0 provisioning.
Cause (SCIM mode): SCIM sync may have failed.
Resolution: Check the SCIM status in the Identity tab. Look for errors in the SCIM error log. Verify the SCIM endpoint is reachable from the IdP.
Role Mapping Not Working
Cause: The claimMappings.roles or claimMappings.groups path does not match the actual JWT claim structure.
Resolution:
- Decode a user's JWT token (use jwt.io or similar).
- Find the actual claim path for groups and roles.
- Update the claim mappings in the tenant's IdP configuration.
- Use "Test mapping" to verify the updated configuration against a sample JWT.
SCIM Provisioning Failing
Cause: Authentication failure, schema mismatch, or network issue.
Resolution:
- Verify the SCIM bearer token has not expired.
- Check the SCIM error log in the Identity tab for specific error messages.
- Ensure the IdP's SCIM client is configured with the correct endpoint URL.
- Verify the IdP is sending SCIM payloads in the expected format (SCIM 2.0 schema).
User Cannot Access Expected Application
Cause: The user's platform role does not grant access to the requested application.
Resolution: Verify the user's role:
consumercan only access Portal.buildercan access Portal and Studio.admincan access all three applications.
Check the role mapping: is the user in the correct IdP group? Use the User Directory to inspect the user's current role and groups.
Related Documentation
- Builder Access Control Guide -- How builders configure agent entitlements using groups and roles
- Admin Model Policies Guide -- Tenant-level model governance
- Troubleshooting Guide -- General platform troubleshooting
- Access Control Reference -- Full technical specification