Skip to content

Zitadel Integration

Zitadel is the recommended identity provider for Shoehorn. It handles authentication via OIDC, provides group claims for team mapping, and supports server-to-server team synchronization via a service user PAT.

  • A running Zitadel instance (self-hosted or cloud)
  • Admin access to create projects, applications, and service users
  • Shoehorn deployed with Helm
  1. Log in to the Zitadel console
  2. Navigate to Projects > Create Project
  3. Name it (e.g., “Shoehorn”)
  4. Note the Project ID from the project details page
  1. In the project, click New Application
  2. Select Web as the application type
  3. Set the Redirect URI: https://shoehorn.example.com/auth/callback
  4. Set the Post-Logout Redirect URI: https://shoehorn.example.com
  5. Note the Client ID

A service user enables server-to-server communication for team and group synchronization.

  1. Navigate to Users > Service Users > New
  2. Create a service user (e.g., “shoehorn-sync”)
  3. Generate a Personal Access Token (PAT)
  4. Grant the service user access to the project:
    • Go to the project
    • Under Authorizations, add the service user

Zitadel includes groups in the groups JWT claim by default. No additional configuration is required.

To verify, decode a JWT token and check for the groups field.

Set these environment variables:

Terminal window
AUTH_PROVIDER=zitadel
ZITADEL_URL=http://zitadel:8080 # Internal URL (cluster-internal)
ZITADEL_EXTERNAL_URL=https://auth.example.com # Browser-accessible URL
ZITADEL_PROJECT_ID=349308689758290610
ZITADEL_CLIENT_ID=349310449335993010
ZITADEL_SERVICE_USER_PAT=your-pat-here # For team sync
auth:
provider: zitadel
zitadel:
projectId: "349308689758290610"
clientId: "349310449335993010"
externalUrl: "https://auth.example.com"
insecure: false
serviceUserPatSecret:
name: zitadel-credentials
key: service-user-pat

Create the Kubernetes secret:

Terminal window
kubectl create secret generic zitadel-credentials \
--from-literal=service-user-pat="your-pat-here"

With the service user PAT configured, Shoehorn can synchronize groups from Zitadel into teams.

  1. Shoehorn queries the Zitadel API for organization groups
  2. Groups are matched to Shoehorn teams via group mappings
  3. When a user logs in, their group memberships update their Shoehorn team assignments

Map a Zitadel group to a Shoehorn team:

Terminal window
curl -X POST https://shoehorn.example.com/api/v1/admin/teams/<team-id>/group-mappings \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"group_id": "engineering-platform",
"idp_provider": "zitadel"
}'

Or via the UI: navigate to Organization > Teams, select a team, go to the Group Mappings tab, and click Add Mapping.

If you use Zitadel alongside other identity providers, enable OrgData sync:

auth:
orgdata:
enabled: true
providers: ["zitadel", "okta"]
primaryProvider: "zitadel" # Conflict resolution priority

When multiple providers are enabled:

  • Users are deduplicated by email
  • Teams are deduplicated by name
  • The primary provider wins conflicts
Terminal window
AUTH_ENCRYPTION_KEY=$(openssl rand -hex 32) # Required in production
SESSION_MAX_AGE=8h # Session validity (default: 8h)
CSRF_ENABLED=true # CSRF protection

Configure how Shoehorn extracts the tenant from Zitadel JWT claims:

StrategyConfigurationUse Case
provider_claimTENANT_ID_CLAIM_KEY=azpSingle tenant (default)
urn_claimTENANT_ID_CLAIM_KEY=urn:zitadel:iam:org:idMulti-org Zitadel
env_varDEFAULT_TENANT_ID=defaultFixed tenant

Login redirects to a blank page

  • Verify ZITADEL_EXTERNAL_URL is browser-accessible (not an internal-only URL)
  • Check the redirect URI matches exactly in both Zitadel and Shoehorn

Groups not appearing in JWT

  • Confirm the user is a member of the group in Zitadel
  • Check the service user has project access
  • Decode the JWT at the /auth/debug endpoint to inspect claims

Team sync not working

  • Verify the service user PAT is valid and not expired
  • Check that group mappings exist for the relevant groups
  • Review API logs for sync errors