ZITADEL Docs
Configure Identity & PoliciesRoles & Permissions

Hardening instance administrators

Terminology Update: We have streamlined our naming conventions to improve clarity. The term Administrators now replaces what was previously referred to as Members, Memberships or Managers. These terms all refer to the same underlying functionality.

Instance administrators (IAM_* roles) can manage resources across every organization on an instance. Organization administrators (ORG_* roles) manage users, projects, and policies within a single organization. When both kinds of principals share the same organization, their authorization scope and login experience overlap in ways that can potentially lead to unexpected account takeover.

This guide describes a practical layout that keeps instance administration separate from customer or application organizations, and how to harden the login client and break-glass access.

Why use a dedicated organization for instance administrators

Create a dedicated organization reserved for instance administrators and automation—not for end users or application teams.

ConcernWhen instance administrators share an application organizationWith a dedicated organization for instance administrators
Authorization scopeOrganization roles such as ORG_USER_MANAGER apply within the same organization where instance administrators may also exist.Instance roles remain on the instance; organization roles in application organizations cannot reach instance-administrator accounts.
Login Behavior and SecurityLogin Behavior and Security can be overridden per organization. Organization Administrators may change MFA, passkeys, external IdPs, or self-registration for the organization where instance-administrator accounts live.You define strict Organization Login Settings only for instance administrators—without coupling them to product organization settings.
Password and lockout settingsPassword complexity, password expiry, and lockout follow the organization context of the user.Instance-administrator accounts inherit the Organization Settings you chose for that dedicated organization, independent of customer organizations.
Operational clarityAuditing who is an Administrator mixes customers and instance administrators in one user list.Instance administrators and service accounts are easy to inventory.

Instance default settings act as fallbacks for organizations. Organization-level settings and policies still override instance defaults for users in that organization. Placing human instance owners or shared break-glass accounts in a customer organization means that organization's administrators can shape how those users authenticate and how local settings behave.

The default organization created at setup is often used for the Management Console and internal projects. Treat it as the dedicated organization for instance administrators only if it does not host customer users and no Organization Administrators manage it on behalf of customer organizations. Otherwise, create a separate organization for instance administration.

Instance
├── Dedicated organization (instance administrators only)
│   ├── Break-glass IAM_OWNER service account (PAT, offline storage)
│   ├── Login client service account (IAM_LOGIN_CLIENT only)
│   └── Optional: other instance automation service accounts
├── Customer / application organization A
│   └── End users, Organization Administrators (ORG_* only)
└── Customer / application organization B
    └── End users, Organization Administrators (ORG_* only)

Guidelines:

  • Do not assign ORG_USER_MANAGER, ORG_OWNER, or similar organization roles to instance administrators or service accounts in the dedicated organization unless they truly manage that organization only.
  • Do not add customer end users to the dedicated organization: users in customer organizations inherit that organization's login and lockout settings, and Organization Administrators can grant organization roles that blur instance-administrator and customer boundaries.
  • Grant instance roles only on the instance administrator list, not as a substitute for organization roles on application resources. For instance service accounts (login client, break-glass), follow Login client (IAM_LOGIN_CLIENT) and review critical permissions in the administrator permission matrix—for example iam.member.write, org.member.write, user.write, user.credential.write, and user.passkey.write. Session permissions (session.write, session.link) are also granted by default and are required for the login UI—do not remove them without testing the full authentication flow. In self-hosted deployments, defaults can be customized via InternalAuthZ.RolePermissionMappings.

Login settings for the dedicated organization

Configure Login Behavior and Security for the dedicated organization explicitly—do not rely on whatever Organization Login Settings happen to apply to a busy application organization.

  1. Open the dedicated organization in the Management Console.
  2. Review Login Behavior and Security (or set Instance Settings first, then override only where needed).
  3. For instance-administrator accounts, consider:
    • Enforcing MFA for local authentication.
    • Restricting external IdPs if instance administrators should only use credentials you control.
    • Disabling self-registration if the organization is not meant for public signup.
    • Tight lockout and password complexity aligned with your instance-administrator baseline.
    • A default redirect URI that does not send instance administrators to a customer application by mistake.

Instance-level and organization-level identity providers are separate from the Organization Login Settings that control whether external login is allowed and which providers are linked for a given organization. An instance IdP can be used by users in an organization only when the effective Organization Login Settings for that organization allow external IdPs (External IDP allowed) and the provider is linked to those settings—or when the organization inherits Instance Settings, which includes the default IdP links. Organizations with custom Organization Login Settings must enable external IdPs themselves and explicitly add instance or organization IdPs to their provider list; see Let users login with preferred identity provider. Document which IdPs instance administrators may use in the dedicated organization.

More context on settings and policy inheritance: Settings and policies.

Login client (IAM_LOGIN_CLIENT)

The self-hosted Login UI authenticates to ZITADEL with a service account that holds the Instance Login Client role (IAM_LOGIN_CLIENT).

Hardening steps:

  • Create the login client user in the dedicated organization for instance administrators, not in a customer or application organization.
  • Use a dedicated service account per login deployment; do not reuse administrator or application service account PATs.
  • Grant only IAM_LOGIN_CLIENT on the instance unless you have a documented exception.
  • Store the PAT in a secrets manager; rotate when the login stack is redeployed or team access changes.
  • Do not use the login client PAT for management automation, CI, or customer integrations.

Setup reference: Create a Login Client and Adopt Login V2.

The default IAM_LOGIN_CLIENT permission set is broad because the login UI finalizes auth requests, manages users during registration, and may synchronize instance roles when a ZITADEL identity provider grants instance roles from token claims. Review the administrator permission matrix before customizing roles in self-hosted deployments.

Break-glass instance owner access

Before changing login routes, features, or Instance Settings, keep a recovery path that does not depend on the interactive login UI.

Avoid lockout

Create a service account in the dedicated organization with the Instance Owner role (IAM_OWNER) and a PAT. Store the PAT offline according to your incident-access procedures. If the login UI or Management Console becomes unreachable, you can still call the Admin API to revert settings.

  • Prefer a service account for break-glass access; limit human IAM_OWNER accounts.
  • Enroll multiple MFA methods for any human instance owners.
  • Invite a second trusted administrator on the instance so access is not tied to a single person.

Self-hosted custom role mappings

If you override InternalAuthZ.RolePermissionMappings in runtime configuration:

  • Diff your mappings against the shipped defaults in cmd/defaults.yaml.
  • Remove permissions that your automation does not need.
  • If you use ZITADEL-as-IdP instance role sync, the login client still needs permission to manage instance administrators for users logging in through that IdP; do not remove those capabilities without testing the support or federation flow you rely on.

Periodic review

Review instance administrators on a schedule and after major organization or login changes.

List instance administrators (Internal Permission Service v2):

curl -sS -X POST \
  "https://${CUSTOM_DOMAIN}/zitadel.internal_permission.v2.InternalPermissionService/ListAdministrators" \
  -H "Authorization: Bearer ${IAM_OWNER_PAT}" \
  -H "Content-Type: application/json" \
  -H "Connect-Protocol-Version: 1" \
  -d '{
    "pagination": { "offset": 0, "limit": 100, "asc": true },
    "filters": [
      { "resource": { "instance": true } }
    ]
  }'

The response includes each user's organizationId and granted roles, which helps verify placement in the dedicated organization.

The Admin API POST /admin/v1/members/_search endpoint is deprecated; prefer ListAdministrators above.

Questions to answer:

  • Does every IAM_* holder belong to the dedicated organization for instance administrators (or a documented exception)?
  • Does the login client service account hold only IAM_LOGIN_CLIENT?
  • Are there human IAM_OWNER accounts in application organizations?
  • Did any instance administrator change since the last review? Use the Event API or stream audit logs to external systems for change history.

Detect grants with Actions v2

Use Actions v2 to get notified when instance administrators change, instead of relying only on scheduled API reviews. Each webhook receives the stored event, including the affected user and role list.

Prerequisites

  • An access token for a user with IAM_OWNER (or another role that can manage Actions targets and executions).
  • A reachable HTTPS endpoint (or Webhook.site for testing).
  • Familiarity with the Actions event guide and payload format.

1. Create a webhook target

curl -sS -X POST "https://${CUSTOM_DOMAIN}/v2/actions/targets" \
  -H "Authorization: Bearer ${IAM_OWNER_PAT}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "name": "instance-administrator-monitor",
    "restWebhook": { "interruptOnError": false },
    "endpoint": "https://your-ops-endpoint.example/webhook/zitadel-instance-members",
    "timeout": "10s"
  }'

Save the returned target ID. Verify inbound requests with the target signing key in production.

2. Register executions for instance member events

Create one execution per event type you want to monitor:

EventWhen it fires
instance.member.addedA user is granted instance administrator roles for the first time
instance.member.changedInstance administrator roles for a user are updated
instance.member.removedInstance administrator roles are removed

Example for new grants:

curl -sS -X PUT "https://${CUSTOM_DOMAIN}/v2/actions/executions" \
  -H "Authorization: Bearer ${IAM_OWNER_PAT}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "condition": {
      "event": {
        "event": "instance.member.added"
      }
    },
    "targets": ["<TARGET_ID>"]
  }'

Repeat for instance.member.changed and instance.member.removed if you want alerts on updates and removals as well.

3. Interpret the webhook payload

The webhook body follows the Actions event payload. For instance member events, relevant fields include:

  • event_type — e.g. instance.member.added
  • aggregateTypeinstance
  • userID — actor who performed the change (when available)
  • event_payload — JSON with at least userId (administrator) and roles (granted instance roles)

Example decoded event_payload for a grant:

{
  "userId": "382586560962387915",
  "roles": ["IAM_LOGIN_CLIENT", "IAM_OWNER"]
}

4. What to flag in your handler

Combine real-time events with the periodic ListAdministrators review above:

  • Any IAM_OWNER or IAM_USER_MANAGER grant outside your dedicated organization for instance administrators (compare userId to your allow-list or resolve organization via API).
  • Login client service account holding roles other than IAM_LOGIN_CLIENT.
  • instance.member.changed where roles includes high-privilege IAM_* roles you did not expect.
  • Grants performed by users or service accounts that are not on your approved administrator list (userID in the outer payload).

Post alerts to your operations channel, SIEM, or ticket system. On ZITADEL Cloud, Actions v2 webhooks are supported; self-hosted installations can use the same API.

See also: Actions v2 examples and streaming audit logs.

Monitor organization administrators in the dedicated organization

Instance IAM_* roles are not the only risk in a mixed layout: organization roles inside the dedicated organization (ORG_USER_MANAGER, ORG_OWNER, …) apply to users in that same organization, including break-glass accounts and service accounts.

Monitor Organization Administrators for the dedicated organization alongside instance administrators.

Periodic review (API)

List Organization Administrators with ListAdministrators, filtered to your dedicated organization:

curl -sS -X POST \
  "https://${CUSTOM_DOMAIN}/zitadel.internal_permission.v2.InternalPermissionService/ListAdministrators" \
  -H "Authorization: Bearer ${IAM_OWNER_PAT}" \
  -H "Content-Type: application/json" \
  -H "Connect-Protocol-Version: 1" \
  -d '{
    "pagination": { "offset": 0, "limit": 100, "asc": true },
    "filters": [
      { "resource": { "organizationId": "${PLATFORM_ORGANIZATION_ID}" } }
    ]
  }'

Questions to answer:

  • Are there ORG_USER_MANAGER or ORG_OWNER grants on the dedicated organization you did not expect?
  • Do any instance administrators or service accounts hold Organization Administrator roles in the dedicated organization without an operational reason?
  • Are customer Organization Administrators accidentally granted roles in the dedicated organization?

In the Management Console, open the dedicated organization and review ADMINISTRATORS in the organization details panel (same UI pattern as ZITADEL Administrators).

Organization member events (Actions v2)

EventWhen it fires
org.member.addedA user is granted organization administrator roles
org.member.changedOrganization administrator roles are updated
org.member.removedOrganization administrator roles are removed

Use the same target setup as for instance member events, then add executions:

curl -sS -X PUT "https://${CUSTOM_DOMAIN}/v2/actions/executions" \
  -H "Authorization: Bearer ${IAM_OWNER_PAT}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "condition": {
      "event": {
        "event": "org.member.added"
      }
    },
    "targets": ["<TARGET_ID>"]
  }'

Repeat for org.member.changed and org.member.removed as needed.

The webhook payload uses the same event structure. For org.member.* events, aggregateType is org and aggregateID is the organization ID. Filter in your handler so you only alert when aggregateID equals your ${PLATFORM_ORGANIZATION_ID}. The event_payload contains userId and roles (for example ORG_USER_MANAGER, ORG_OWNER).

Flag grants such as unexpected ORG_USER_MANAGER on the dedicated organization, or any Organization Administrator role on users that should be service accounts only.

Review changes with the Event API

If you already pull audit data into a SIEM or batch job, the Event API is an alternative to Actions v2 webhooks. Poll on a schedule (for example every few minutes) and process new rows since the last sequence.

Requires IAM_OWNER or IAM_OWNER_VIEWER.

Instance administrator events

curl -sS -X POST "https://${CUSTOM_DOMAIN}/admin/v1/events/_search" \
  -H "Authorization: Bearer ${IAM_OWNER_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "asc": true,
    "limit": 1000,
    "sequence": 0,
    "aggregate_types": ["instance"],
    "event_types": [
      "instance.member.added",
      "instance.member.changed",
      "instance.member.removed"
    ]
  }'

Increase sequence on subsequent runs to the highest sequence returned previously so you only fetch new events.

Dedicated organization administrator events

Filter by your dedicated organization ID as resource_owner and restrict to organization member events:

curl -sS -X POST "https://${CUSTOM_DOMAIN}/admin/v1/events/_search" \
  -H "Authorization: Bearer ${IAM_OWNER_PAT}" \
  -H "Content-Type: application/json" \
  -d '{
    "asc": true,
    "limit": 1000,
    "sequence": 0,
    "resource_owner": "${PLATFORM_ORGANIZATION_ID}",
    "aggregate_types": ["org"],
    "event_types": [
      "org.member.added",
      "org.member.changed",
      "org.member.removed"
    ]
  }'

Parse each event payload for userId and roles, then apply the same allow-list checks as in the Actions handlers above.

Actions v2 vs Event API: Actions push events to your endpoint in near real time with minimal setup. The Event API fits centralized log pipelines, replay, and environments where outbound webhooks are undesirable. You can use both.

Checklist

  • Dedicated organization for instance administrators exists; customer users are not members.
  • Login Behavior and Security for the dedicated organization enforces your instance-administrator authentication baseline (MFA, IdPs, lockout).
  • Login client service account lives in the dedicated organization with a dedicated PAT.
  • Break-glass IAM_OWNER PAT is stored offline and tested periodically.
  • Custom RolePermissionMappings (if any) are reviewed against least privilege.
  • Organization Administrators (ORG_*) on the dedicated organization are reviewed and monitored (org.member.* events or Event API).
  • Instance administrators are reviewed regularly and Actions v2 monitors instance.member.* events (or changes are pulled via the Event API / SIEM).

Was this page helpful?

On this page