Dex IdP custom token claims from Postgres
Consider a multi-tenant SaaS app. After login, your app still needs to know:
- which org is this user in
- what's their role
- are they on a paid plan
- ... ...
And users may authenticate through different connectors (password, LDAP, external IdP) - the token needs consistent user context regardless.
This post covers a Dex fork that enriches issued tokens with custom claims - statically or from a Postgres query.
Checkout the changes at github.com/edgeflare/dex.
Or use the image right-away ghcr.io/edgeflare/dex with the environment variables.
How it works
A CustomClaims map[string]any field is added to storage.Claims. Enrichers write into it; the token pipeline reads it out. Any type implementing this interface participates in token issuance:
type ClaimsEnricher interface {
Enrich(ctx context.Context, claims *storage.Claims) error
}
Multiple enrichers can be chained with NewClaimsEnricherChain - later enrichers overwrite earlier ones on key conflicts. Errors abort the chain and block token issuance entirely.
Static enricher
Set DEX_CUSTOM_CLAIMS_STATIC to a JSON object:
DEX_CUSTOM_CLAIMS_STATIC='{"policy": {"pgrole": "authn"}}'
Postgres enricher
Configured via environment variables. Each column in the result row becomes a claim.
| Variable | Description | Example |
|---|---|---|
DEX_CUSTOM_CLAIMS_POSTGRES_URI | libpq connection uri | postgres://dex:secret@pg:5432/users?sslmode=require |
DEX_CUSTOM_CLAIMS_POSTGRES_QUERY | SQL returning one row | SELECT role, org, plan FROM users WHERE user_id = $1 AND email = $2 LIMIT 1 |
DEX_CUSTOM_CLAIMS_POSTGRES_QUERY_ARGS | JSON array of Go templates | ["{{.UserID}}","{{.Email}}"] |
Available template fields (used in QUERY_ARGS):
{{.UserID}} {{.Email}} {{.Username}} {{.PreferredUsername}}
The resulting token includes the query columns alongside standard claims:
{
"iss": "https://dex.example.com",
"sub": "Cg0xMjM0NTY3ODkwEgVsb2NhbA",
"email": "[email protected]",
"email_verified": true,
"role": "owner",
"org": "acme",
"plan": "enterprise"
}
Reserved claims (iss, sub, aud, etc.) are always protected - enrichers cannot overwrite them.