Skip to main content

Zitadel

OIDC/SSO identity provider for RavenmaskOS.


Overview

Zitadel provides centralized authentication using OIDC/OAuth2, serving as the single source of truth for user identity across all services.

PropertyValue
Imageghcr.io/zitadel/zitadel:latest
Containerzitadel
URLauth.ravenhelm.dev
Port8080 (internal)
Config~/ravenhelm/services/zitadel/
DatabasePostgreSQL (zitadel database)

Architecture

External Request

Traefik (TLS)

Zitadel:8080

PostgreSQL:5432
(zitadel DB)

OIDC Endpoints

EndpointURL
Issuerhttps://auth.ravenhelm.dev
Discoveryhttps://auth.ravenhelm.dev/.well-known/openid-configuration
Authorizationhttps://auth.ravenhelm.dev/oauth/v2/authorize
Tokenhttps://auth.ravenhelm.dev/oauth/v2/token
Userinfohttps://auth.ravenhelm.dev/oidc/v1/userinfo
JWKShttps://auth.ravenhelm.dev/oauth/v2/keys
End Sessionhttps://auth.ravenhelm.dev/oidc/v1/end_session

Registered Applications

ApplicationClient IDTypeRedirect URI
OAuth2-Proxy351314046345084937Web (PKCE)https://oauth.ravenhelm.dev/oauth2/callback
Norns Admin350642335773687817Webhttps://norns.ravenhelm.dev/api/auth/callback/zitadel
Grafana351369960024506377Webhttps://grafana.ravenhelm.dev/login/generic_oauth
GitLab351312537486163977Webhttps://gitlab.ravenhelm.dev/users/auth/openid_connect/callback
Bifrost(configured)Webhttps://bifrost.ravenhelm.dev/api/auth/callback/zitadel

Admin Access

URL: https://auth.ravenhelm.dev
Username: admin@ravenhelm.auth.ravenhelm.dev
Password: (stored in 1Password: "Zitadel Admin")

Configuration

docker-compose.yml

services:
zitadel:
image: ghcr.io/zitadel/zitadel:latest
container_name: zitadel
restart: unless-stopped
command: 'start-from-init --masterkey "${ZITADEL_MASTERKEY}" --tlsMode external'
environment:
ZITADEL_DATABASE_POSTGRES_HOST: postgres
ZITADEL_DATABASE_POSTGRES_PORT: 5432
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: ${POSTGRES_USER}
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: ${POSTGRES_PASSWORD}
ZITADEL_EXTERNALSECURE: "true"
ZITADEL_EXTERNALPORT: 443
ZITADEL_EXTERNALDOMAIN: auth.ravenhelm.dev
ZITADEL_FIRSTINSTANCE_ORG_NAME: ravenhelm
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: admin
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ZITADEL_ADMIN_PASSWORD}
ZITADEL_MASTERKEY: ${ZITADEL_MASTERKEY}
networks:
- ravenhelm_net
labels:
- "traefik.enable=true"
- "traefik.http.routers.zitadel.rule=Host(`auth.ravenhelm.dev`)"
- "traefik.http.routers.zitadel.entrypoints=websecure"
- "traefik.http.routers.zitadel.tls.certresolver=letsencrypt"
- "traefik.http.services.zitadel.loadbalancer.server.port=8080"
- "traefik.http.services.zitadel.loadbalancer.server.scheme=h2c"

Environment Variables

VariablePurpose
ZITADEL_MASTERKEYEncryption key for secrets (32 hex chars)
ZITADEL_ADMIN_PASSWORDInitial admin user password
ZITADEL_EXTERNALDOMAINPublic domain for OIDC issuer
ZITADEL_EXTERNALSECUREEnable HTTPS (true)
ZITADEL_EXTERNALPORTPublic port (443)

Add New OIDC Application

Via Web UI

  1. Login to auth.ravenhelm.dev
  2. Navigate to OrganizationProjects → Select project
  3. Click + New Application
  4. Choose Web Application
  5. Select authentication method:
    • PKCE (recommended for SPAs/public clients)
    • Code (for confidential clients with secrets)
  6. Configure:
    • Redirect URIs: https://service.ravenhelm.dev/callback
    • Post Logout URIs: https://service.ravenhelm.dev
  7. Save and copy Client ID (and Secret if applicable)

Common Redirect URI Patterns

FrameworkPattern
NextAuth.js/api/auth/callback/zitadel
OAuth2-Proxy/oauth2/callback
Grafana/login/generic_oauth
GitLab/users/auth/openid_connect/callback

Email Configuration

SMTP configured for AWS SES:

# /data/zitadel/smtp-setup.yaml
smtp:
host: email-smtp.us-east-1.amazonaws.com
port: 587
tls: true
from: noreply@ravenhelm.dev
fromName: RavenmaskOS
user: (AWS SES credentials)
password: (AWS SES credentials)

Quick Commands

# Check health
docker exec zitadel /app/zitadel ready

# View logs
docker logs -f zitadel

# Restart
docker restart zitadel

# Test OIDC discovery
curl -s https://auth.ravenhelm.dev/.well-known/openid-configuration | jq .

# Check database connection
docker exec -i postgres psql -U ravenhelm -d zitadel -c "SELECT COUNT(*) FROM projections.users8;"

Database

Zitadel uses a dedicated PostgreSQL database:

# Access Zitadel database
docker exec -i postgres psql -U ravenhelm -d zitadel

# Check user count
SELECT COUNT(*) FROM projections.users8;

# List organizations
SELECT id, name FROM projections.orgs1;

# List applications
SELECT id, name, client_id FROM projections.apps7_oidc_configs;

Troubleshooting

Login Loop

Symptoms: Redirects back to login after authenticating

Diagnosis:

docker logs zitadel 2>&1 | grep -i "error\|redirect"

Solutions:

  1. Verify redirect URI matches exactly (case-sensitive, no trailing slash)
  2. Check Client ID/Secret in service configuration
  3. Clear browser cookies for .ravenhelm.dev
  4. Verify ZITADEL_EXTERNALDOMAIN matches URL

Token Invalid

Symptoms: Services reject Zitadel tokens

Diagnosis:

# Test token endpoint
curl -X POST https://auth.ravenhelm.dev/oauth/v2/token \
-d "grant_type=client_credentials" \
-d "client_id=<id>" \
-d "client_secret=<secret>"

Solutions:

  1. Verify EXTERNALDOMAIN matches public URL
  2. Check TLS termination (Traefik should handle)
  3. Verify token scopes match application config

Database Connection Failed

Symptoms: Zitadel won't start, database errors

Diagnosis:

docker logs zitadel 2>&1 | grep -i "postgres\|database"

Solutions:

  1. Verify PostgreSQL is running: docker ps | grep postgres
  2. Check database exists: docker exec postgres psql -U ravenhelm -l | grep zitadel
  3. Verify credentials in .env file

Master Key Issues

Symptoms: Encryption/decryption errors

Solutions:

  1. Master key must be exactly 32 hex characters
  2. Key cannot be changed after initialization
  3. If key is lost, Zitadel must be reinitialized (data loss)

Backup & Restore

Backup

# Backup Zitadel database
docker exec postgres pg_dump -U ravenhelm -d zitadel | gzip > zitadel_$(date +%Y%m%d).sql.gz

Restore

# Restore (requires dropping existing)
docker exec postgres dropdb -U ravenhelm zitadel
docker exec postgres createdb -U ravenhelm zitadel
gunzip -c zitadel_backup.sql.gz | docker exec -i postgres psql -U ravenhelm -d zitadel

References