This guide documents authentication configuration in Posit Team Operator. Team Operator supports multiple authentication methods for both Posit Connect and Posit Workbench.
- Overview
- Authentication Types
- OIDC Configuration
- SAML Configuration
- Password Authentication
- Role-Based Access Control
- Keycloak Integration
- Secrets Management
- Troubleshooting
Team Operator uses the AuthSpec structure to configure authentication for Posit products. Authentication is configured per-product (Connect and Workbench) through the auth field in each product's spec.
The complete AuthSpec type definition:
type AuthSpec struct {
Type AuthType `json:"type,omitempty"`
ClientId string `json:"clientId,omitempty"`
Issuer string `json:"issuer,omitempty"`
Groups bool `json:"groups,omitempty"`
UsernameClaim string `json:"usernameClaim,omitempty"`
EmailClaim string `json:"emailClaim,omitempty"`
UniqueIdClaim string `json:"uniqueIdClaim,omitempty"`
GroupsClaim string `json:"groupsClaim,omitempty"`
DisableGroupsClaim bool `json:"disableGroupsClaim,omitempty"`
SamlMetadataUrl string `json:"samlMetadataUrl,omitempty"`
SamlIdPAttributeProfile string `json:"samlIdPAttributeProfile,omitempty"`
SamlUsernameAttribute string `json:"samlUsernameAttribute,omitempty"`
SamlFirstNameAttribute string `json:"samlFirstNameAttribute,omitempty"`
SamlLastNameAttribute string `json:"samlLastNameAttribute,omitempty"`
SamlEmailAttribute string `json:"samlEmailAttribute,omitempty"`
Scopes []string `json:"scopes,omitempty"`
ViewerRoleMapping []string `json:"viewerRoleMapping,omitempty"`
PublisherRoleMapping []string `json:"publisherRoleMapping,omitempty"`
AdministratorRoleMapping []string `json:"administratorRoleMapping,omitempty"`
}Team Operator supports three authentication types:
| Type | Value | Use Case |
|---|---|---|
| Password | password |
Development, simple deployments |
| OpenID Connect (OIDC) | oidc |
Enterprise SSO with OAuth2/OpenID Connect |
| Security Assertion Markup Language (SAML) | saml |
Enterprise SSO with SAML 2.0 |
OpenID Connect (OIDC) is recommended for enterprise deployments.
apiVersion: core.posit.team/v1beta1
kind: Site
metadata:
name: production
namespace: posit-team
spec:
connect:
auth:
type: "oidc"
clientId: "connect-client-id"
issuer: "https://idp.example.com"Before configuring OIDC in Team Operator, configure your Identity Provider:
- Create an OAuth2/OIDC Application in your IdP
- Configure Redirect URIs:
- Connect:
https://connect.example.com/__login__/callback - Workbench:
https://workbench.example.com/oidc/callback
- Connect:
- Note the Client ID (used in the spec)
- Generate a Client Secret (stored in secrets)
The client secret must be stored in your secrets provider:
For Kubernetes secrets:
- Connect:
pub-client-secretkey - Workbench:
dev-client-secretkey
For AWS Secrets Manager:
- Connect:
pub-client-secretin your vault - Workbench:
dev-client-secretin your vault
Configure how OIDC claims map to user attributes:
spec:
connect:
auth:
type: "oidc"
clientId: "connect-client-id"
issuer: "https://idp.example.com"
usernameClaim: "preferred_username" # Claim for username
emailClaim: "email" # Claim for email
uniqueIdClaim: "sub" # Claim for unique identifierDefault behavior:
- If
emailClaimis set butuniqueIdClaimis not, the email claim is used for unique ID - Default
uniqueIdClaimisemail
Enable group synchronization from your IdP:
spec:
connect:
auth:
type: "oidc"
clientId: "connect-client-id"
issuer: "https://idp.example.com"
groups: true # Enable group auto-provisioning
groupsClaim: "groups" # Claim containing group membership
scopes:
- "openid"
- "profile"
- "email"
- "groups" # Scope to request groupsDisabling Groups Claim:
Some IdPs do not support a groups claim. Disable it explicitly:
spec:
connect:
auth:
type: "oidc"
clientId: "connect-client-id"
issuer: "https://idp.example.com"
groups: true # Still auto-provision groups
disableGroupsClaim: true # But don't try to read from tokenOverride the default OIDC scopes:
spec:
connect:
auth:
type: "oidc"
clientId: "connect-client-id"
issuer: "https://idp.example.com"
scopes:
- "openid"
- "profile"
- "email"
- "offline_access"spec:
connect:
auth:
type: "oidc"
clientId: "0oaxxxxxxxxx"
issuer: "https://your-org.okta.com"
usernameClaim: "preferred_username"
emailClaim: "email"
groups: true
groupsClaim: "groups"
scopes:
- "openid"
- "profile"
- "email"
- "groups"spec:
connect:
auth:
type: "oidc"
clientId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
issuer: "https://login.microsoftonline.com/{tenant-id}/v2.0"
usernameClaim: "preferred_username"
emailClaim: "email"
uniqueIdClaim: "oid" # Azure object ID
groups: true
groupsClaim: "groups"
scopes:
- "openid"
- "profile"
- "email"Note: Azure AD requires specific application permissions to include group claims. Configure "Groups claim" in the Token configuration.
spec:
connect:
auth:
type: "oidc"
clientId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
issuer: "https://your-tenant.auth0.com/"
usernameClaim: "email"
emailClaim: "email"
groups: true
groupsClaim: "https://your-namespace/groups" # Custom claim namespace
scopes:
- "openid"
- "profile"
- "email"spec:
connect:
auth:
type: "oidc"
clientId: "connect"
issuer: "https://keycloak.example.com/realms/posit"
usernameClaim: "preferred_username"
emailClaim: "email"
groups: true
groupsClaim: "groups"
scopes:
- "openid"
- "profile"
- "email"
- "groups"SAML 2.0 authentication is supported for enterprise environments.
spec:
connect:
auth:
type: "saml"
samlMetadataUrl: "https://idp.example.com/saml/metadata"Required:
samlMetadataUrlmust be set for SAML authentication.
Team Operator supports two approaches for SAML attribute mapping.
Use a predefined attribute profile matching your IdP:
spec:
connect:
auth:
type: "saml"
samlMetadataUrl: "https://idp.example.com/saml/metadata"
samlIdPAttributeProfile: "azure" # Options: default, azure, etc.Built-in profiles:
default- Standard SAML attributesazure- Microsoft Azure AD attributes
Specify individual attribute URIs for complete control:
spec:
connect:
auth:
type: "saml"
samlMetadataUrl: "https://idp.example.com/saml/metadata"
samlUsernameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
samlFirstNameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
samlLastNameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
samlEmailAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"Important:
samlIdPAttributeProfileand individual attribute mappings are mutually exclusive. The operator will return an error if both are specified.
Configure your IdP with these Service Provider details:
Connect:
- Entity ID:
https://connect.example.com/__login__ - ACS URL:
https://connect.example.com/__login__/callback
Workbench:
- Entity ID:
https://workbench.example.com/saml - ACS URL:
https://workbench.example.com/saml/acs
spec:
connect:
auth:
type: "saml"
samlMetadataUrl: "https://login.microsoftonline.com/{tenant-id}/federationmetadata/2007-06/federationmetadata.xml"
samlIdPAttributeProfile: "azure"Or with custom attributes:
spec:
connect:
auth:
type: "saml"
samlMetadataUrl: "https://login.microsoftonline.com/{tenant-id}/federationmetadata/2007-06/federationmetadata.xml"
samlUsernameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"
samlEmailAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
samlFirstNameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
samlLastNameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"spec:
connect:
auth:
type: "saml"
samlMetadataUrl: "https://your-org.okta.com/app/xxxxxxxx/sso/saml/metadata"
samlUsernameAttribute: "NameID"
samlEmailAttribute: "email"
samlFirstNameAttribute: "firstName"
samlLastNameAttribute: "lastName"Workbench SAML uses the usernameClaim field for the username attribute:
spec:
workbench:
auth:
type: "saml"
samlMetadataUrl: "https://idp.example.com/saml/metadata"
usernameClaim: "email" # Maps to auth-saml-sp-attribute-usernamePassword authentication is the simplest method, suitable for development environments.
spec:
connect:
auth:
type: "password"
workbench:
auth:
type: "password"- Development and testing environments
- Quick proof-of-concept deployments
- Environments without enterprise SSO requirements
- Password authentication stores credentials in the product's database
- Not recommended for production environments with security requirements
- Does not provide SSO capabilities
- User management must be done within each product
Team Operator supports automatic role mapping based on IdP group membership.
Connect supports three roles:
- Viewer - Can view published content
- Publisher - Can publish and manage content
- Administrator - Full administrative access
Configure role mappings:
spec:
connect:
auth:
type: "oidc"
clientId: "connect-client-id"
issuer: "https://idp.example.com"
groups: true
groupsClaim: "groups"
viewerRoleMapping:
- "connect-viewers"
- "readonly-users"
publisherRoleMapping:
- "connect-publishers"
- "data-scientists"
administratorRoleMapping:
- "connect-admins"
- "platform-admins"- When a user logs in, Connect reads their group membership from the
groupsClaim - The user is assigned the highest matching role:
- If any group matches
administratorRoleMapping-> Administrator - Else if any group matches
publisherRoleMapping-> Publisher - Else if any group matches
viewerRoleMapping-> Viewer - Else -> Default role (configured separately)
- If any group matches
Role mappings work the same way with SAML authentication, provided your IdP sends group membership in the SAML assertion.
Workbench uses admin groups for administrative access:
spec:
workbench:
# Admin groups have access to the administrative dashboard
adminGroups:
- "workbench-admin"
- "platform-admins"
# Superuser groups have elevated administrative privileges
adminSuperuserGroups:
- "workbench-superusers"Set the default role for users who don't match any role mapping:
spec:
connect:
config:
Authorization:
DefaultUserRole: "viewer" # Options: viewer, publisher, administratorTeam Operator can deploy and manage a Keycloak instance for authentication.
apiVersion: core.posit.team/v1beta1
kind: Site
metadata:
name: production
namespace: posit-team
spec:
keycloak:
enabled: true
image: "quay.io/keycloak/keycloak:latest"
imagePullPolicy: IfNotPresentWhen enabled, Team Operator does the following:
- Deploys a Keycloak instance in the namespace
- Creates a PostgreSQL database for Keycloak
- Configures ingress routing to
key.<domain> - Sets up service accounts and RBAC
Configure products to use the deployed Keycloak:
spec:
keycloak:
enabled: true
connect:
auth:
type: "oidc"
clientId: "connect"
issuer: "https://key.example.com/realms/posit"
groups: true
groupsClaim: "groups"After Keycloak deploys, complete these steps:
- Access the Keycloak admin console at
https://key.<domain> - Create a realm (e.g., "posit")
- Create clients for each product
- Configure client credentials and redirect URIs
- Set up user federation if needed (LDAP, AD, etc.)
Authentication requires properly configured secrets in your secrets provider.
For secret.type: kubernetes, create a secret with the required keys:
apiVersion: v1
kind: Secret
metadata:
name: site-secrets
namespace: posit-team
type: Opaque
stringData:
# Connect OIDC
pub-client-secret: "your-connect-client-secret"
# Workbench OIDC
dev-client-secret: "your-workbench-client-secret"
dev-admin-token: "generated-admin-token"
dev-user-token: "generated-user-token"For secret.type: aws, store secrets in AWS Secrets Manager:
| Secret Key | Description |
|---|---|
pub-client-secret |
Connect OIDC client secret |
dev-client-secret |
Workbench OIDC client secret |
dev-admin-token |
Workbench admin authentication token |
dev-user-token |
Workbench user authentication token |
| Product | Auth Type | Secret Key | Purpose |
|---|---|---|---|
| Connect | OIDC | pub-client-secret |
OAuth2 client secret |
| Workbench | OIDC | dev-client-secret |
OAuth2 client secret |
| Workbench | OIDC | dev-admin-token |
Admin API token |
| Workbench | OIDC | dev-user-token |
User API token |
Cause: The redirect URI in the IdP doesn't match what the product sends.
Solution: Verify redirect URIs are configured exactly:
- Connect:
https://<connect-url>/__login__/callback - Workbench:
https://<workbench-url>/oidc/callback
Cause: Groups claim not configured or not included in token.
Debug steps:
- Verify
groups: trueis set - Verify
groupsClaimmatches what your IdP sends - Verify the
groupsscope is requested - Check if your IdP requires special configuration for group claims
Enable OIDC logging for Connect:
spec:
connect:
debug: true # Enables OAuth2 loggingCause: Claims mapping doesn't match IdP token.
Solution: Verify your IdP token contains the expected claims:
spec:
connect:
auth:
usernameClaim: "preferred_username" # Must exist in token
emailClaim: "email" # Must exist in tokenCause: The SAML metadata URL is unreachable from the cluster.
Solutions:
- Verify the metadata URL is accessible from pods
- Verify network policies allow outbound connections
- Verify DNS resolution works
Cause: Both samlIdPAttributeProfile and individual attributes are set.
Solution: Use one approach:
# Option 1: Use profile
samlIdPAttributeProfile: "azure"
# Option 2: Use individual attributes
samlUsernameAttribute: "..."
samlEmailAttribute: "..."Debug steps:
- Check the SAML assertion from your IdP
- Verify attribute names match exactly (case-sensitive)
- Use full URIs for standard attributes:
samlUsernameAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"
To debug OIDC token claims:
-
Enable Debug Logging:
spec: connect: debug: true
-
Check Pod Logs:
kubectl logs -n posit-team deploy/<site>-connect -f
-
Decode JWT Tokens: Use jwt.io to inspect tokens and verify claims.
If users aren't getting the correct roles:
-
Verify group claim is present:
- Check the
groupsClaimfield matches your IdP - Some IdPs use nested claims (e.g.,
realm_access.roles)
- Check the
-
Check group name matching:
- Group names in role mappings must match exactly
- Group names are case-sensitive
-
Verify IdP configuration:
- Verify groups are included in the token
- Check token size limits (large group lists may be truncated)
Workbench may include port numbers in redirect URIs. The operator sets a header to prevent this:
X-Rstudio-Request: https://<workbench-url>If you see port 443 in redirect URIs, verify Traefik middleware is correctly applied.
For Workbench with OIDC/SAML:
spec:
workbench:
createUsersAutomatically: true # Create system users on first loginA complete Site configuration with OIDC authentication:
apiVersion: core.posit.team/v1beta1
kind: Site
metadata:
name: production
namespace: posit-team
spec:
domain: posit.example.com
secret:
type: "kubernetes"
vaultName: "production-secrets"
connect:
image: ghcr.io/rstudio/rstudio-connect:ubuntu22-2024.10.0
auth:
type: "oidc"
clientId: "connect-production"
issuer: "https://login.microsoftonline.com/tenant-id/v2.0"
usernameClaim: "preferred_username"
emailClaim: "email"
uniqueIdClaim: "oid"
groups: true
groupsClaim: "groups"
scopes:
- "openid"
- "profile"
- "email"
viewerRoleMapping:
- "Connect-Viewers"
publisherRoleMapping:
- "Connect-Publishers"
- "Data-Scientists"
administratorRoleMapping:
- "Connect-Admins"
workbench:
image: ghcr.io/rstudio/rstudio-workbench:jammy-2024.12.0
createUsersAutomatically: true
auth:
type: "oidc"
clientId: "workbench-production"
issuer: "https://login.microsoftonline.com/tenant-id/v2.0"
usernameClaim: "preferred_username"
scopes:
- "openid"
- "profile"
- "email"
adminGroups:
- "Workbench-Admins"
adminSuperuserGroups:
- "Platform-Admins"- Product Team Site Management - Complete Site configuration guide
- Posit Connect Admin Guide - Connect authentication documentation
- Posit Workbench Admin Guide - Workbench authentication documentation