Zitadel
Your login and grant store — the identity Atrium runs on.
Capability: identity · Identity. This is a core capability: the platform asks it questions, so exactly one connected instance is marked Primary and answers. The first instance you enable becomes Primary automatically; a second one never steals the role.
Configure Zitadel in Atrium → (admins only)
Setup
The management credential Atrium uses to provision grants, reconcile group membership and read projects. This is the write side of Atrium → Zitadel — never the login/OIDC client secret.
- Create a service user. In Zitadel, create a machine (service) user that represents Atrium, e.g. "atrium-management". Open the console page ↗
- Grant it the right manager role. Assign IAM_OWNER at the instance level (needed for Action Targets & Executions and cross-org reads). An ORG_OWNER token also works for org-scoped calls and goes in the Service-admin field.
- Generate a Personal Access Token. On the service user → Personal Access Tokens → New. Copy the token value once and paste it into the IAM token field below. (A service-account key JSON is the last-resort fallback.)
What you paste back into Atrium
In order, the values the steps above produce:
- IAM token (PAT) — or an Org-Owner Service-admin token
How it works
This is the WRITE side of Atrium to Zitadel, and only that. The login path (which OIDC client users sign in through) is deploy-time config read by NextAuth before any database or session exists, so it is deliberately not editable here: a database-backed login secret would be a lockout waiting to happen. What this credential does is provision: it turns Atrium's model of who belongs to which group into Zitadel grants.
What Atrium does, and when
| When this happens | Atrium does this |
|---|---|
| A person is granted access to an app, or joins a group that confers it | Creates or updates the user grant on the app's Zitadel project with the resulting roles. |
| A person loses that access | Removes the grant. Sign-in for that app fails at the identity provider from then on, not just in Atrium's own UI. |
| An admin opens an app or group screen | Reads projects, roles and existing grants so the screen shows Zitadel's actual state rather than Atrium's cached opinion of it. |
| A reconcile pass runs over a group | Fans out into one management call per member. The 30-second resolver cache exists specifically so this fan-out does not re-read the credential row per call. |
Guarantees and limits
- The three credential fields are alternatives, not a set. Exactly one is needed: the IAM token is preferred, then the service-admin token, then the service-account key JSON. A row with none of them reads as unconfigured rather than half-wired.
- Instance-level operations (Action Targets and Executions, cross-org reads) require IAM_OWNER. An ORG_OWNER token is enough for org-scoped work (grants, projects, users) and nothing more.
- Identity is a core capability: exactly one connected instance is Primary and answers.
What "Test connection" proves
Performs an authenticated read against the management API with the token as supplied. A pass proves the token is valid and has at least read scope. It does not prove the token can write grants: an under-scoped ORG_OWNER token passes the probe and then fails on the first instance-level call.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Grants are created but the person still cannot sign in. | The app's Zitadel project has projectRoleCheck (or hasProjectCheck) set in a way the grant does not satisfy, or the user's session predates the grant. | Check the project's settings in Zitadel, then have the person sign out of Atrium properly (or use a private window). Re-logging in at Zitadel alone does not refresh an Atrium cookie. |
| Everything worked, then every management call started failing at once. | The PAT expired or was revoked on the service user. | Generate a new Personal Access Token on the same service user and paste it here. No redeploy needed. |
| Zitadel bills far more API calls than expected. | A reconcile path is issuing one call per member instead of a bulk read. | A code-side concern, not a config one: reconcilers should read a group's members with one bulk-index call rather than one call per member. |
Required scopes
Grant the credential exactly these scopes; they are the least-privilege set the connector needs:
IAM_OWNER (iamToken)ORG_OWNER (serviceAdminToken)
Configuration fields
| Field | Type | Required | Notes |
|---|---|---|---|
iamTokenIAM token | passwordsecret | no | PAT / service-account token with the IAM_OWNER role (instance scope: Action Targets & Executions). Maps to ZITADEL_IAM_TOKEN. Preferred token for all management calls. |
serviceAdminTokenService admin token | passwordsecret | no | Org Owner token — fallback for org-scoped calls (grants, projects, users). Maps to ZITADEL_SERVICE_ADMIN_TOKEN. |
serviceAccountKeyService-account key (JSON) | passwordsecret | no | Optional machine-key JSON ({type,keyId,key,userId}) for the JWT-assertion flow. Last-resort fallback when no bearer token is set. Maps to ZITADEL_SERVICE_ACCOUNT_KEY. |
Operating notes
- Secret fields are encrypted at rest (AES-256-GCM) and never returned to the client. Editing an instance and leaving a secret field masked keeps the stored value.
- Adding a connector from the marketplace creates a disabled, credential-less instance ("Added, not yet connected"); it does nothing until an admin opens it, pastes credentials, and enables it.
- Use Test connection on the instance page before enabling: it performs a read-only probe with the credentials you entered.
- Connector credentials live in the Atrium database, not in container env vars; changing them here takes effect without a redeploy (within the resolver's 30-second cache).