Need-to-KnowNeed-to-Know
SecurityAuthenticationKeycloakAuth0

Keycloak vs Auth0 for Canton Validators

A practical comparison of identity providers for Canton Network validators, based on real production experience running both.

10 min read

When setting up authentication for a Canton validator, you'll need an OIDC-compliant identity provider. Auth0 and Keycloak are the two most common choices. We ran both in production and ultimately chose Keycloak—here's what we learned.

💡Context
This isn't a "Keycloak is always better" piece. Auth0 is excellent for many use cases. But Canton validators have specific requirements that shift the calculus.

The Bottom Line

ConsiderationAuth0Keycloak
Setup speed✅ Faster initial setupRequires K8s deployment
Claim customizationActions (JS-based)✅ Protocol mappers (declarative)
Audience mapperRequires custom Action✅ Built-in, first-class
M2M auth✅ Works well✅ Works well
Self-hosted❌ SaaS only✅ Full control
Cost at scalePer-MAU pricing✅ Infrastructure cost only
Canton fitWorkable✅ Better aligned

What Canton Actually Needs from Your IdP

Before comparing providers, understand what Canton's authentication layer requires:

  1. Stable sub claims — The wallet binds user identity permanently. If sub changes, users lose access to their wallets.
  2. Custom aud (audience) claims — Canton expects tokens withhttps://canton.network.global (or your custom audience) in the access token.
  3. M2M (machine-to-machine) tokens — The validator backend needs to authenticate to the participant Ledger API using client credentials flow.
  4. JWKS endpoint — Both validator and participant need to fetch public keys to verify tokens.

Both Auth0 and Keycloak can do all of this. The difference is how.

The Audience Claim Problem

This is where the providers diverge most visibly for Canton operators.

Auth0 Approach

Auth0 requires you to create an API resource and configure your application to request that API's identifier as the audience. Alternatively, you write a custom Action to inject audiences:

Auth0 Action to add audience
exports.onExecutePostLogin = async (event, api) => {
  api.accessToken.setCustomClaim('aud', [
    event.client.client_id,
    'https://canton.network.global'
  ]);
};

This works, but Actions execute JavaScript in Auth0's infrastructure, and debugging them requires navigating Auth0's dashboard and logs.

Keycloak Approach

Keycloak has a built-in Audience protocol mapper. You create a client scope, add the mapper, and attach it to your clients:

Keycloak audience mapper (declarative)
Client Scope: canton-audience
  └─ Mapper: Audience
       Name: canton-audience
       Included Custom Audience: https://canton.network.global
       Add to access token: ON

No code. Fully declarative. Exportable as JSON. Version-controllable.

Why This Matters
When your authentication breaks at 2am, you want declarative configuration you can diff and rollback—not JavaScript functions running in someone else's cloud.

Identity Migration: The Hidden Risk

Here's a scenario that catches operators off guard: you start with Auth0 for fast prototyping, then realize you need to switch to Keycloak (or vice versa).

Problem: Auth0 and Keycloak generate different sub claim formats:

ProviderTypical sub format
Auth0google-oauth2|123456789012345678901
Keycloaka1b2c3d4-e5f6-7890-abcd-ef1234567890

If you switch IdPs, existing wallet users can't log in—their identity is bound to the old sub.

The Keycloak Escape Hatch

Keycloak's hardcoded claim mapper can force a specific sub value per user, enabling migration from any previous IdP:

Hardcoded sub mapper for migration
{
  "name": "legacy-sub-override",
  "protocol": "openid-connect",
  "protocolMapper": "oidc-hardcoded-claim-mapper",
  "config": {
    "claim.name": "sub",
    "claim.value": "google-oauth2|123456789012345678901",
    "access.token.claim": "true"
  }
}

Auth0 can do something similar with Actions, but the declarative Keycloak approach is easier to audit, version control, and apply per-user when needed.

Self-Hosting: Control vs. Convenience

Auth0 is SaaS-only. Keycloak runs wherever you want.

Why Self-Hosting Matters for Validators

  • Data sovereignty — Some jurisdictions require authentication data to stay in-region
  • Network topology — Keycloak can run inside your cluster, reducing external dependencies and latency for token validation
  • Availability — Your validator doesn't go down because Auth0 has an outage
  • Cost predictability — No per-MAU pricing surprises as your user base grows

The Keycloak Deployment Reality

Self-hosting isn't free. You need:

  • A PostgreSQL database (or built-in H2 for dev)
  • Kubernetes resources (or a VM)
  • TLS termination and ingress configuration
  • Backup and upgrade procedures

For teams already running Kubernetes for their validator, adding Keycloak is incremental. For teams without K8s experience, Auth0's managed service has real value.

Keycloak Helm deployment (simplified)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install keycloak bitnami/keycloak \
  --set auth.adminUser=admin \
  --set auth.adminPassword=<secure-password> \
  --set postgresql.enabled=true

Machine-to-Machine Authentication

Both providers handle M2M well. The validator backend authenticates to the participant Ledger API using client credentials flow—this works identically in both:

Client credentials token request
curl -X POST https://your-idp/oauth/token \
  -d "grant_type=client_credentials" \
  -d "client_id=validator-backend" \
  -d "client_secret=<secret>" \
  -d "audience=https://canton.network.global"

The main difference: Auth0 charges for M2M tokens in some plans, while Keycloak has no per-token cost.


Cost Comparison

ScenarioAuth0Keycloak
10 users (dev/test)Free tier~$20/mo (small K8s)
1,000 MAU~$230/mo (Essentials)~$50/mo (K8s + DB)
10,000 MAU~$1,000+/mo~$100/mo (same infra)
M2M tokensPlan-dependent limitsUnlimited

For validators expecting growth, Keycloak's flat infrastructure cost becomes increasingly attractive.

Why We Chose Keycloak

We started with Auth0 for speed. It worked. But as we moved toward production:

  1. Audience mapping friction — Debugging Auth0 Actions was slower than editing Keycloak mappers
  2. Configuration drift — Auth0's UI-first approach made it hard to version control our IdP config; Keycloak exports to JSON
  3. Cost trajectory — Our user growth projections made Auth0's pricing less attractive
  4. Operational consistency — We were already running everything else in Kubernetes; adding Keycloak kept our operational model uniform
💡When Auth0 Makes Sense
If you're a small team without Kubernetes experience, prioritizing time-to-market over long-term cost, Auth0 is a legitimate choice. The migration path exists if you need it later.

Recommendations

Choose Auth0 If:

  • You need to ship in days, not weeks
  • Your team lacks Kubernetes/infrastructure experience
  • You have a small, stable user base
  • You're prototyping and may pivot away from Canton

Choose Keycloak If:

  • You're already running Kubernetes
  • You need full control over claim customization
  • You expect user growth or heavy M2M token usage
  • You want version-controlled, auditable IdP configuration
  • You may need to migrate users from another IdP