oidc zitadel - mescon/Muximux GitHub Wiki
OIDC: Zitadel
Zitadel is a self-hosted (or managed) cloud-native identity platform with a multi-organization model. It's used as a Keycloak alternative in Kubernetes and self-hosted setups.
This guide sets up Muximux to authenticate against Zitadel with group memberships emitted in the ID token via Zitadel's project-roles mechanism.
Prerequisites
- A Zitadel instance, either self-hosted or on
zitadel.cloud. You need an account with permission to create projects and applications in your organization. - Muximux on a stable URL.
Step 1: Create the Project
In Zitadel, applications live inside projects, and projects belong to organizations.
- Sign in to Zitadel admin, pick the org you want Muximux under, and open Projects > Create.
- Name:
Muximux. - Open the new project. On the General tab:
- Enable Assert Roles on Authentication. This is what makes role memberships ride in the ID token.
- Optionally enable Check Roles on Authentication to refuse sign-in for users with no role at all in this project.
Step 2: Define Roles
Roles in Zitadel are project-scoped strings; Muximux will receive them in the urn:zitadel:iam:org:project:roles claim. To use them as Muximux groups, set groups_claim to that exact path.
- In the project, open Roles > New.
- Create at least:
- Key:
muximux-admin, Display Name:Muximux Admin - Key:
muximux-user, Display Name:Muximux User
- Key:
- Add any other roles you'll later use for per-app filtering, e.g.
engineering,on-call.
Step 3: Create the OIDC Application
- Inside the project, open New Application > Web > Next.
- Authentication method: CODE (authorization code with a client secret).
- Redirect URIs:
https://muximux.example.com/api/auth/oidc/callback - Post Logout Redirect URIs:
https://muximux.example.com/login(or wherever you want users to land after logout). - Continue and copy the Client ID and Client Secret Zitadel generates. Both are needed for
config.yaml.
After creation, open the application's Token Settings tab and confirm:
- Auth Token Type:
JWT - Add user roles to the access token: enabled (only relevant if you'll consume access tokens; harmless either way)
- User roles inside ID Token: enabled. This is the important toggle for Muximux.
Step 4: Grant Roles to Users
Users get project roles via Authorizations, either directly or through a Zitadel project grant.
- In the project, open Authorizations > New.
- Pick the user, choose the role you want them to have (e.g.
muximux-admin). - Save. Repeat for any other users.
For groups of users, create a Zitadel Group, assign roles to the group, and add users to the group.
Step 5: Configure Muximux
auth:
method: oidc
oidc:
enabled: true
issuer_url: https://your-zitadel.example.com
client_id: <client id from Step 3>
client_secret: ${ZITADEL_CLIENT_SECRET}
redirect_url: https://muximux.example.com/api/auth/oidc/callback
scopes:
- openid
- profile
- email
- urn:zitadel:iam:org:project:id:zitadel:aud
username_claim: preferred_username
email_claim: email
# The Zitadel claim that carries project roles. Muximux treats its keys
# as the user's group list; the values are role display metadata that
# Muximux ignores.
groups_claim: "urn:zitadel:iam:org:project:roles"
display_name_claim: name
admin_groups:
- muximux-admin
The unusual scope (urn:zitadel:iam:org:project:id:zitadel:aud) tells Zitadel that Muximux's audience is the Zitadel project itself; without it, ID token validation can fail in strict-audience mode. Replace zitadel in that scope name with your project's resource ID if you have a different one.
issuer_url is the base URL of your Zitadel instance, no trailing path.
Set ZITADEL_CLIENT_SECRET in the environment. Restart Muximux.
Using Settings
Instead of editing config.yaml, you can open Settings > Security, choose Single sign-on (OIDC), and enter the base URL of your Zitadel instance as the issuer URL, the client ID from Step 3, the extra audience scope shown above added to the scopes, urn:zitadel:iam:org:project:roles as the groups claim, and your role key (for example muximux-admin) under admin groups. Copy the Callback URL (register at the provider) and Back-channel logout URL rows into the provider, press Test connection, then press Update Method. See Authentication for how the form handles the client secret and SSO-only sign-in.
Step 6: Validate
- Visit
https://muximux.example.com/loginand click Login with SSO. - Sign in to Zitadel and approve the consent screen if shown.
- Confirm a
muximux-adminuser sees the Settings gear. - To inspect what Zitadel actually emitted: in Zitadel, open Project > Application > Token Inspector and exchange a fresh code; the decoded ID token shows the
urn:zitadel:iam:org:project:rolesmap.
Sign-out
Zitadel supports RP-initiated logout at /oidc/v1/end_session (listed as end_session_endpoint in discovery) and OIDC back-channel logout.
- Return address. The
post_logout_redirect_urimust match one of the post-logout URIs registered on the application exactly. Addhttps://muximux.example.com/login?logged_out=1(or yourpost_logout_redirect_url) to the application's post-logout redirect URIs (the Redirect Settings section of the application in Console), and setprovider_logout: true. - Back-channel logout. Register
https://muximux.example.com/api/auth/oidc/backchannel-logoutas the application's back-channel logout URI (backchannel_logout_uri). The label of this field in Console could not be confirmed from the documentation consulted; check your Zitadel version's application settings. Once set, Zitadel notifies Muximux for sessions started after the setting was saved.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Sign-in fails with audience not allowed or invalid token |
The audience scope from Step 5 is missing or names the wrong project. | Open the application's URLs tab in Zitadel; the scope it expects is shown there. Copy verbatim. |
| Sign-in works but admin gear is missing | Either User roles inside ID Token is off, or groups_claim doesn't match the URN. |
Re-check Step 3's token settings, then verify groups_claim: is exactly urn:zitadel:iam:org:project:roles. |
| User has no role and sign-in is refused | Check Roles on Authentication is on and the user has no project authorization. | Either grant the user a role (Step 4), or turn off Check Roles on Authentication to allow role-less sign-in. |
unauthorized_client |
Authentication method on the application doesn't match (e.g. set to NONE but Muximux is sending a secret). |
Edit the application and set Authentication method to CODE. |
See Also
- Authentication overview for the full
auth.oidcreference. - Other identity providers: Microsoft Entra ID, Keycloak, Authentik, Pocket ID, Google, Authelia, Cloudflare Access.