This guide covers authentication configuration for the SEA Workbench across different environments (development, staging, production).
The Workbench uses a dual-layer authentication architecture:
Reference:
1
2
3
4
5
6
7
8
9
10
11
┌─────────────────┐ OIDC Flow ┌──────────┐
│ Workbench UI │ ←───────────────→ │ Zitadel │
│ (Vite/React) │ │ (OIDC) │
└────────┬────────┘ └──────────┘
│ │
│ Bearer Token │
↓ │
┌─────────────────┐ │
│ Workbench BFF │ ──────────────────────────────┘
│ (FastAPI) │ JWT Validation
└─────────────────┘
| Environment | Frontend Provider | Backend Provider | Mock Mode |
|---|---|---|---|
| Development | zitadel/mock |
stub |
Allowed |
| Staging | zitadel |
oidc |
Disabled |
| Production | oidc |
oidc |
Disabled |
For UI development without authentication overhead:
Frontend (.env.local)
1
2
VITE_AUTH_PROVIDER=mock
VITE_AUTH_MOCK=true
Backend (Environment)
1
2
AUTH_PROVIDER=stub
ENVIRONMENT=development
This bypasses all OIDC flows and returns a mock user with viewer role.
For testing authentication with a local Zitadel instance:
1. Start Zitadel via Docker
1
2
3
4
5
docker run -d \
--name zitadel \
-p 8080:8080 \
-e ZITADEL_DATABASE_STROAGE_POSTGRES_HOST=db \
ghcr.io/zitadel/zitadel:latest
2. Configure Frontend (.env.local)
1
2
3
4
5
6
VITE_AUTH_PROVIDER=zitadel
VITE_AUTH_MOCK=false
VITE_OIDC_AUTHORITY=http://localhost:8080
VITE_OIDC_CLIENT_ID=your-client-id
VITE_OIDC_REDIRECT_URI=http://localhost:3000/callback
VITE_OIDC_POST_LOGOUT_URI=http://localhost:3000
3. Configure Backend
1
2
3
4
5
AUTH_PROVIDER=oidc
OIDC_ISSUER_URL=http://localhost:8080
OIDC_CLIENT_ID=your-client-id
OIDC_AUDIENCE=your-audience
ENVIRONMENT=development
For testing with a public OIDC provider (Auth0, Keycloak, etc.):
Frontend (.env.local)
1
2
3
4
5
VITE_AUTH_PROVIDER=oidc
VITE_AUTH_MOCK=false
VITE_OIDC_AUTHORITY=https://your-issuer.auth0.com
VITE_OIDC_CLIENT_ID=your-client-id
VITE_OIDC_REDIRECT_URI=http://localhost:3000/callback
Backend
1
2
3
4
AUTH_PROVIDER=oidc
OIDC_ISSUER_URL=https://your-issuer.auth0.com
OIDC_CLIENT_ID=your-client-id
OIDC_AUDIENCE=your-api-identifier
Staging should mirror production as closely as possible:
Frontend (.env.production)
1
2
3
4
5
VITE_AUTH_PROVIDER=zitadel
VITE_AUTH_MOCK=false
VITE_OIDC_AUTHORITY=https://staging-auth.your-domain.com
VITE_OIDC_CLIENT_ID=staging-client-id
VITE_OIDC_REDIRECT_URI=https://staging.workbench.your-domain.com/callback
Backend (Environment Variables)
1
2
3
4
5
ENVIRONMENT=staging
AUTH_PROVIDER=oidc
OIDC_ISSUER_URL=https://staging-auth.your-domain.com
OIDC_CLIENT_ID=staging-client-id
OIDC_AUDIENCE=staging-audience
Key Points:
CRITICAL: Production enforces OIDC-only authentication with fail-fast validation.
ENVIRONMENT=productionAUTH_PROVIDER=oidc (backend)VITE_AUTH_PROVIDER=oidc or zitadel (frontend)VITE_AUTH_MOCK=false.env.production
1
2
3
4
5
6
VITE_AUTH_PROVIDER=oidc
VITE_AUTH_MOCK=false
VITE_OIDC_AUTHORITY=https://auth.your-domain.com
VITE_OIDC_CLIENT_ID=production-client-id
VITE_OIDC_REDIRECT_URI=https://workbench.your-domain.com/callback
VITE_OIDC_POST_LOGOUT_URI=https://workbench.your-domain.com
Environment Variables
1
2
3
4
5
ENVIRONMENT=production
AUTH_PROVIDER=oidc
OIDC_ISSUER_URL=https://auth.your-domain.com
OIDC_CLIENT_ID=production-client-id
OIDC_AUDIENCE=production-audience
Both frontend and backend enforce production safety:
Backend (services/workbench-bff/src/api/auth.py)
1
2
3
4
5
6
7
8
9
10
def validate_auth_config():
"""Fail-fast validation on app startup"""
env = os.getenv("ENVIRONMENT", "development")
if env == "production":
if os.getenv("AUTH_PROVIDER") != "oidc":
raise ValueError("AUTH_PROVIDER must be 'oidc' in production")
required_vars = ["OIDC_ISSUER_URL", "OIDC_CLIENT_ID", "OIDC_AUDIENCE"]
missing = [v for v in required_vars if not os.getenv(v)]
if missing:
raise ValueError(f"Missing required OIDC variables: {missing}")
Frontend (apps/workbench/src/lib/auth-factory.ts)
1
2
3
4
5
6
7
8
if (import.meta.env.MODE === 'production') {
const provider = import.meta.env.VITE_AUTH_PROVIDER;
const mockEnabled = import.meta.env.VITE_AUTH_MOCK === 'true';
if ((provider === 'zitadel' || provider === 'oidc') && mockEnabled) {
throw new Error('VITE_AUTH_MOCK cannot be enabled in production with OIDC provider');
}
}
In Zitadel Console:
SEA Workbenchworkbench-uihttp://localhost:3000/callbackhttps://staging.workbench.your-domain.com/callbackhttps://workbench.your-domain.com/callbackhttp://localhost:3000https://staging.workbench.your-domain.comhttps://workbench.your-domain.comviewer, operator, adminSymptom: Browser blocks login redirect with CORS error
Solution:
http:// or https://) and portSymptom: Backend rejects token with signature verification error
Solution:
OIDC_ISSUER_URL matches Zitadel issuer exactlyhttps://your-issuer.com/.well-known/jwks.jsonSymptom: Build fails with error about VITE_AUTH_MOCK in production
Solution:
1
2
# Set this explicitly in production build
VITE_AUTH_MOCK=false
The frontend will throw a build error if mock mode is enabled in production.
Symptom: Backend crashes on startup with ValueError
Solution:
Ensure all required variables are set when ENVIRONMENT=production:
1
2
3
4
5
ENVIRONMENT=production
AUTH_PROVIDER=oidc
OIDC_ISSUER_URL=https://...
OIDC_CLIENT_ID=your-client-id
OIDC_AUDIENCE=your-audience
Symptom: User can’t access features despite having roles
Solution:
auth.py1
2
3
4
5
6
7
# Start with mock mode
npm run dev
# Should login automatically with mock user
# Start with Zitadel
VITE_AUTH_PROVIDER=zitadel VITE_AUTH_MOCK=false npm run dev
# Should redirect to Zitadel login
1
2
3
4
5
# With stub provider
curl -H "X-User-Id: testuser" http://localhost:8010/health
# With OIDC provider
curl -H "Authorization: Bearer <jwt-token>" http://localhost:8010/health
1
2
3
4
5
6
7
# Set production environment
export ENVIRONMENT=production
# Remove AUTH_PROVIDER (should fail)
unset AUTH_PROVIDER
python services/workbench-bff/main.py
# Expected: ValueError: AUTH_PROVIDER must be 'oidc' in production
.env.local for local development only1
2
3
4
5
# Frontend
grep VITE_AUTH .env.local
# Backend
env | grep -E '(AUTH_PROVIDER|OIDC|ENVIRONMENT)'
1
2
curl https://your-issuer.com/.well-known/openid-configuration
curl https://your-issuer.com/.well-known/jwks.json
1
2
# Decode and view token claims
echo "your-jwt-token" | jq -R 'split(".") | .[1] | @base64d | fromjson'
For authentication issues:
services/workbench-bff/logs/)