How to migrate from Zitadel 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.

This migration temporarily interrupts user access to PhariaAI. Plan your migration during a maintenance window and communicate the schedule to your users.


Why migrate to Dex?

We recommend migrating to Dex for two reasons:

  • Zitadel recently changed their license to AGPL 3.0.

  • 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

In this section:

Federation-only approach

Dex is a federation-only identity provider. This 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 apart from 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 do not 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 the following:

  • Administrative access to your PhariaAI installation

  • Access to your Helm values configuration

  • An operational external OIDC identity provider (Google, Microsoft, Okta, and so on); this is mandatory for Dex

  • Client ID and Client Secret from your OIDC provider

  • Kubectl access to your Kubernetes cluster

  • A PostgreSQL database for Dex:

    • For production: An existing external PostgreSQL database with proper backup

    • For testing: Can use the included PostgreSQL instance (dexPostgresql)

Do not delete the Zitadel PostgreSQL database until you have fully validated the Dex migration and are confident you do not need to roll back. Deleting the Zitadel database means it is impossible to roll back to Zitadel.

Migration overview

The migration process consists of the following phases:

Phase 1: Preparation

Document current configuration

Document external identity provider settings

If you are using external identity providers (such sa Google, Microsoft, Okta, and so on), document the following:

  • 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, and so on), 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. We recommend to use the included PostgreSQL instance for testing and 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.

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 following example 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"
  • The redirectURI uses 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

  1. Update your Helm values file with the Dex and PhariaAI IAM configuration from Phase 2.

  2. Deploy the changes:

    helm upgrade pharia-ai <chart-name> \
        --namespace <your-namespace> \
        -f values.yaml \
        --wait
  3. Verify Dex is running:

    kubectl get pods -n <your-namespace> | grep dex

    You will see the Dex pod in a Running state:

    pharia-iam-dex-xxxxx                    1/1     Running   0          2m

    If using the included PostgreSQL (for test deployments), you will also see:

    pharia-iam-dex-postgresql-0             1/1     Running   0          2m
  4. 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 will see a message similar to the following:

{"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 4.

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

Now verify the response. You should see a JSON response with OIDC configuration including the following:

  • issuer: Should be https://pharia-iam.<YOUR_DOMAIN>/oidc

  • authorization_endpoint: Should be https://pharia-iam.<YOUR_DOMAIN>/oidc/auth

  • token_endpoint: Should be 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:

  1. 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
  2. You should be redirected to your OIDC provider: The browser redirects you to your configured identity provider (Google, Microsoft, Okta, and so on).

  3. Authenticate with your credentials: Log in using your identity provider credentials.

  4. Verify the redirect back: After successful authentication, you will be redirected back to:

    https://pharia-assistant.<YOUR_DOMAIN>/callback?code=<authorization-code>&state=test123

You may see an error page at this callback URL. This is because PhariaAssistant has not yet been configured to use Dex. However, the important indicators of success are the following:

  • ✅ You were successfully redirected to your OIDC provider.

  • ✅ You could authenticate with your provider.

  • ✅ You were redirected back with a code parameter in the URL.

Now check the 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.

Troubleshooting: 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.

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

  1. Check that Zitadel pods are terminated:

    kubectl get pods -n <your-namespace> | grep zitadel

    You should see no Zitadel pods running or pods in Terminating state. Note that the Zitadel PostgreSQL pod may still be running at this point.

  2. Verify Dex is still running:

    kubectl get pods -n <your-namespace> | grep dex

    Dex pods should remain in Running state.

  3. Test login to PhariaAI applications:

    1. Navigate to a PhariaAI application. For example: https://pharia-assistant.<YOUR_DOMAIN>.

    2. Click the login button.
      You should be redirected to Dex at https://pharia-iam.<YOUR_DOMAIN>/oidc/auth.

    3. Complete authentication with your identity provider.

    4. Verify you are successfully logged into the application

  4. Verify user roles are assigned correctly:

    1. Check that users can access resources according to their roles.

    2. Ensure default roles are applied for new users (if configured in pharia-iam.config.defaultRolesForLogin).

Disable Zitadel PostgreSQL (if previously enabled)

Database removal warning: Do not proceed with disabling Zitadel PostgreSQL until you have thoroughly tested Dex authentication and are certain you will not need to roll back. Once the Zitadel PostgreSQL database is disabled and its data removed, rollback to Zitadel is no longer possible.

After verifying that Dex is working correctly and all users can authenticate, you can 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 roll back to Zitadel, you can re-enable it. This is only possible if you have not 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

  1. Check that Zitadel pods are running:

    kubectl get pods -n <your-namespace> | grep zitadel

    You should see Zitadel pods (and optionally PostgreSQL pods) in Running state.

  2. Test login with Zitadel:

    1. Navigate to https://login.<YOUR_DOMAIN> (or your configured Zitadel URL).

    2. Verify you can access the Zitadel interface.

    3. Test login with existing Zitadel accounts.

    4. Verify all user roles and permissions are intact.

  3. Test PhariaAI application login:

    1. Navigate to a PhariaAI application.

    2. Verify you can log in successfully.

    3. 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 product support with the following information:

  • Your Dex configuration (please redact sensitive information).

  • Relevant logs from Dex and PhariaAI IAM components.

  • Description of the issue and steps to reproduce.