How to migrate from Zitadel as IDP to Dex
This guide walks you through the process of migrating your PhariaAI identity provider from Zitadel to Dex. Dex is a federated OpenID Connect (OIDC) provider that can integrate with multiple authentication backends while providing a single unified authentication interface.
Why migrate to Dex?
As Zitadel changed there license to AGPL 3.0, and Dex installations are easier to maintain, Zitadel will no longer be supported in future versions of PhariaAI. All users must migrate to Dex to continue receiving updates and support. Plan your migration as soon as possible.
Understanding authentication changes
Federation-only approach
Dex is a federation-only identity provider, which means it does not store user credentials directly. Instead, it acts as a bridge to external identity providers (such as Google, Microsoft, Okta, or any OIDC-compliant provider).
Key differences from Zitadel:
- No username/password authentication: Unlike Zitadel, Dex does not support local username/password credentials besides simple test users. All users must authenticate through an external identity provider.
- External identity provider required: You must have an external OIDC identity provider configured and operational before migrating to Dex.
- No self-registration: Users cannot create accounts directly in PhariaAI. User accounts are managed by your external identity provider.
If you were previously using Zitadel with username/password credentials (self-registration), you need to migrate your user credentials to an external identity provider before switching to Dex. If you don't currently operate an external identity provider, you may install Zitadel following the official Zitadel documentation and set up a federation from Dex to Zitadel.
User data migration
When both Zitadel and Dex are running in parallel, relevant user data (excluding credentials) are automatically migrated. (see Phase 3: Verify automatic user migration).
What is preserved:
- User IDs, roles, and permissions (matched by email)
- Access to resources and data
What changes:
- Authentication method (now via external identity provider)
Ensure all user email addresses in Zitadel are unique and verified. Users must have accounts in your external identity provider with matching email addresses.
Prerequisites
This migration requires PhariaAI v1.251100.1 or higher.
Before starting the migration, ensure you have:
- Administrative access to your PhariaAI installation
- Access to your Helm values configuration
- An operational external OIDC identity provider (Google, Microsoft, Okta, etc.) - This is mandatory for Dex
- Client ID and Client Secret from your OIDC provider
- Kubectl access to your Kubernetes cluster
- PostgreSQL database for Dex:
- For production: An existing external PostgreSQL database with proper backup
- For testing: Can use the included PostgreSQL instance (
dexPostgresql)
This migration will temporarily interrupt user access to PhariaAI. Plan your migration during a maintenance window and communicate the schedule to your users.
Do not delete the Zitadel PostgreSQL database until you have fully validated the Dex migration and are confident you won't need to rollback. Deleting the Zitadel database will make it impossible to rollback to Zitadel.
Migration overview
The migration process consists of the following phases:
- Preparation: Document identity provider settings
- Configuration: Set up Dex with your identity provider
- Deployment: Deploy Dex
- Validation: Test OIDC authentication flow
- Switch to Dex: Disable Zitadel and switch all frontends to Dex
Phase 1: Preparation
Document current configuration
Document external identity provider settings
If you are using external identity providers (Google, Microsoft, Okta, etc.), document:
- Client IDs and secrets
- Redirect URIs
- Scopes and claims
- Any custom user attribute mappings
Configure redirect URI in your external identity provider
In your external identity provider (Google, Microsoft, Okta, etc.), whitelist the following redirect URI:
https://pharia-iam.<YOUR_DOMAIN>/oidc/callback
Replace <YOUR_DOMAIN> with your configured ingress domain. Without this, authentication will fail with an "invalid redirect URI" error.
Phase 2: Configure Dex
Configure PostgreSQL database for Dex
Dex requires a PostgreSQL database for persistent storage. You can either use the included PostgreSQL instance for testing or connect to an external database for production deployments.
Option A: Use included PostgreSQL database (for test deployments only)
For testing and development environments, you can enable the included PostgreSQL database:
pharia-iam:
dexPostgresql:
enabled: true
The included PostgreSQL database (dexPostgresql) is intended for test deployments only. For production environments, use an external PostgreSQL database with proper backup and high-availability configurations.
Option B: Use external PostgreSQL database (recommended for production)
For production deployments, connect to an existing external PostgreSQL database by overriding the environment variables:
pharia-iam:
dexPostgresql:
enabled: false
dex:
envVars:
- name: "DB_PASSWORD"
valueFrom:
secretKeyRef:
name: "<your-secret-name>"
key: "password"
- name: "DB_NAME"
value: "<your-database-name>"
- name: "DB_HOST"
value: "<your-database-host>"
- name: "DB_USER"
value: "<your-database-user>"
The database connection configuration is merged with the defaults in values.yaml. You only need to overwrite the specific values (credentials and host) for your external database.
Set up Dex with OIDC connector
Enable Dex and configure your OIDC connector. The example below uses Google as the identity provider:
pharia-iam:
dex:
enabled: true
config:
# Configure your OIDC connector
# For other connector options please refer to dex documentation https://dexidp.io/docs/connectors/
connectors:
- type: oidc
id: google
name: Google
config:
issuer: https://accounts.google.com
clientID: $CLIENT_ID
clientSecret: $CLIENT_SECRET
redirectURI: https://pharia-iam.{{ .Values.global.ingress.ingressDomain }}/oidc/callback
scopes:
- openid
- profile
- email
getUserInfo: true
envVars:
- name: "CLIENT_ID"
valueFrom:
secretKeyRef:
name: "<your-secret-name>"
key: "client-id"
- name: "CLIENT_SECRET"
valueFrom:
secretKeyRef:
name: "<your-secret-name>"
key: "client-secret"
Note:
- The
redirectURIuses a template variable that will be automatically populated with your configured ingress domain. - Other Dex configuration (storage, static clients, environment variables) is already pre-configured in the default
values.yaml. :::
Phase 3: Deploy Dex
Apply the new configuration
-
Update your Helm values file with the Dex and PhariaAI IAM configuration from Phase 2.
-
Deploy the changes:
helm upgrade pharia-ai <chart-name> \
--namespace <your-namespace> \
-f values.yaml \
--wait -
Verify Dex is running:
kubectl get pods -n <your-namespace> | grep dexYou should see the Dex pod in a
Runningstate:pharia-iam-dex-xxxxx 1/1 Running 0 2mIf using the included PostgreSQL (test deployments), you'll also see:
pharia-iam-dex-postgresql-0 1/1 Running 0 2m -
Check Dex logs for any configuration errors:
kubectl logs -n <your-namespace> -l app.kubernetes.io/name=dex
Verify automatic user migration
When both Zitadel and Dex are enabled, PhariaAI automatically migrates all users from Zitadel during pod startup.
Check the pharia-iam logs for successful migration:
kubectl logs -n <your-namespace> -l app.kubernetes.io/name=pharia-iam | grep "pharia_iam::iam::users::migration"
You should see a message like:
{"level":"INFO","fields":{"message":"User migration completed","total_migrated":4777,"total_failed":0,"failed_user_ids":"[]"}}
Verify that total_failed is 0 and failed_user_ids is empty ("[]") before proceeding to Phase 5.
Phase 4: Test authentication
Before deploying Dex to production, verify that the OIDC authentication flow works correctly by performing a manual authorization code flow test.
Verify Dex discovery endpoint
First, confirm that Dex is accessible and properly configured:
-
Open the OIDC discovery endpoint in your browser:
Navigate to:
https://pharia-iam.<YOUR_DOMAIN>/oidc/.well-known/openid-configuration -
Verify the response:
You should see a JSON response with OIDC configuration including:
issuer: Should behttps://pharia-iam.<YOUR_DOMAIN>/oidcauthorization_endpoint:https://pharia-iam.<YOUR_DOMAIN>/oidc/authtoken_endpoint:https://pharia-iam.<YOUR_DOMAIN>/oidc/token
This confirms Dex is running and accessible.
Test the authorization code flow
Perform a manual OIDC authorization code flow using the pre-configured pharia-assistant client:
-
Initiate the authorization flow:
Open the following URL in your browser (replace
<YOUR_DOMAIN>with your configured ingress domain):https://pharia-iam.<YOUR_DOMAIN>/oidc/auth?client_id=pharia-assistant&redirect_uri=https://pharia-assistant.<YOUR_DOMAIN>/callback&response_type=code&scope=openid+profile+email&state=test123 -
You should be redirected to your OIDC provider:
The browser will redirect you to your configured identity provider (Google, Microsoft, Okta, etc.)
-
Authenticate with your credentials:
Log in using your identity provider credentials
-
Verify the redirect back:
After successful authentication, you will be redirected back to:
https://pharia-assistant.<YOUR_DOMAIN>/callback?code=<authorization-code>&state=test123You may see an error page at this callback URL (since PhariaAssistant isn't configured to use Dex yet), but the important indicators of success are:
- ✅ You were successfully redirected to your OIDC provider
- ✅ You could authenticate with your provider
- ✅ You were redirected back with a
codeparameter in the URL
-
Check Dex logs for successful authentication:
kubectl logs -n <your-namespace> -l app.kubernetes.io/name=dex | grep -i "login successful"You should see a log entry indicating that the login with the external provider succeeded.
Common issues during testing
- "Client not found" error: Ensure the static clients are configured in Dex and not overridden (they should already be configured by default in
values.yaml) - "Invalid redirect URI": Verify your DNS is configured correctly and the domain matches exactly
- Connection refused: Check that Dex ingress is properly configured and TLS certificates are valid
- Not redirected to identity provider: Check the connector configuration and verify your OIDC provider is accessible
- "invalid_request" error: Verify the URL parameters are correct and properly URL-encoded
If the manual test succeeds, Dex is properly configured and ready to use. If it fails, review the troubleshooting section below.
Phase 5: Switch to Dex
Once you have verified that Dex is working correctly through the manual test, you can disable Zitadel and switch all PhariaAI frontends to use Dex as the identity provider.
Disable Zitadel
Update your Helm values to disable Zitadel:
pharia-iam:
zitadel:
enabled: false
Apply the changes
Deploy the updated configuration:
helm upgrade pharia-ai <chart-name> \
--namespace <your-namespace> \
-f values.yaml \
--wait
Verify the deployment
-
Check that Zitadel pods are terminated:
kubectl get pods -n <your-namespace> | grep zitadelYou should see no Zitadel pods running (or pods in
Terminatingstate). The Zitadel PostgreSQL pod may still be running at this point. -
Verify Dex is still running:
kubectl get pods -n <your-namespace> | grep dexDex pods should remain in
Runningstate. -
Test login to PhariaAI applications:
- Navigate to a PhariaAI application (e.g.,
https://pharia-assistant.<YOUR_DOMAIN>) - Click the login button
- You should be redirected to Dex at
https://pharia-iam.<YOUR_DOMAIN>/oidc/auth - Complete authentication with your identity provider
- Verify you are successfully logged into the application
- Navigate to a PhariaAI application (e.g.,
-
Verify user roles are assigned correctly:
- Check that users can access resources according to their roles
- Ensure default roles are applied for new users (if configured in
pharia-iam.config.defaultRolesForLogin)
Disable Zitadel PostgreSQL (if previously enabled)
Do not proceed with disabling Zitadel PostgreSQL until you have thoroughly tested Dex authentication and are certain you won't need to rollback. Once the Zitadel PostgreSQL database is disabled and its data is removed, rollback to Zitadel will be impossible.
After verifying that Dex is working correctly and all users can authenticate, you can also disable the Zitadel PostgreSQL database if it was enabled before:
pharia-iam:
zitadelPostgresql:
enabled: false
Apply the changes:
helm upgrade pharia-ai <chart-name> \
--namespace <your-namespace> \
-f values.yaml \
--wait
Rollback procedure
If you encounter critical issues after switching to Dex and need to rollback to Zitadel, you can re-enable it. This is only possible if you haven't deleted the Zitadel PostgreSQL data.
Re-enable Zitadel
Update your Helm values to re-enable Zitadel:
pharia-iam:
config:
adminEnableZitadelManagement: true
zitadel:
enabled: true
zitadelPostgresql:
enabled: true # Only if you were using the included PostgreSQL
Apply the rollback
Deploy the rollback configuration:
helm upgrade pharia-ai <chart-name> \
--namespace <your-namespace> \
-f values.yaml \
--wait
Verify Zitadel is working
-
Check that Zitadel pods are running:
kubectl get pods -n <your-namespace> | grep zitadelYou should see Zitadel pods (and optionally PostgreSQL pods) in
Runningstate. -
Test login with Zitadel:
- Navigate to
https://login.<YOUR_DOMAIN>(or your configured Zitadel URL) - Verify you can access the Zitadel interface
- Test login with existing Zitadel accounts
- Verify all user roles and permissions are intact
- Navigate to
-
Test PhariaAI application login:
- Navigate to a PhariaAI application
- Verify you can log in successfully
- Check that all functionality works as expected
Additional resources
Support
If you encounter issues during the migration that are not covered in this guide, please contact Aleph Alpha support with:
- Your Dex configuration (redact sensitive information)
- Relevant logs from Dex and PhariaAI IAM components
- Description of the issue and steps to reproduce