# Admin roles (RBAC)

Scope exactly what each administrator can see and do with fine-grained, role-based access control.

## What it is

Not every administrator should hold the keys to everything. HelixIAM's admin RBAC grants **admin permissions to the realm's [roles](../manage/roles.md)** — so who can administer what is driven by the same roles you already assign to users. A help-desk operator role might reset credentials but never touch signing keys; a realm-owner role manages its own realm but not the platform.

Grants are drawn from a fixed catalogue of discrete **permissions**, each gating a class of administrative action (managing users, editing realm settings, managing clients, viewing events, and so on). The `realm-admin` permission is the super-grant. Assign the smallest set that lets an admin do their job — least privilege by construction.

Every realm is seeded with sensible defaults: the `admin` role holds `realm-admin`; the `auditor` role holds the read-only `view-*` permissions; the default `user` role holds none.

## In the console

1. Open **Manage → Users & access → Admin roles**.
2. Pick a [realm role](../manage/roles.md) and tick the admin permissions it should grant.
3. **Save** — the grants take effect on the next request from anyone holding that role.

Admins see only the screens and actions their role's permissions allow; everything else is hidden or denied.

## How to design roles

- Start from the permission catalogue and group by job function (operator, realm owner, auditor, security).
- Separate **read** from **write** where the catalogue allows it — the seeded `auditor` role (view-only) is the canonical example. The split isn't total: `view-identity-providers` only covers trusted lists, so identity-provider configs and secrets still need `manage-identity-providers` even to read.
- Keep highly privileged permissions (`realm-admin`, `manage-realm`, `manage-events`) on a small number of tightly held roles.
- Pair RBAC with the [audit log](observability.md) so every administrative write is attributable to a named admin.

## Over the API

Admin RBAC lives under `/admin/realms/{realm}/admin-roles`. It edits the permission grants **on existing realm roles** — there is no separate admin-role object to create or delete. The examples below assume you have set `$HELIX_URL` / `$REALM` and logged in — see [Authenticating to the API](../getting-started/api-authentication.md).

### List roles and their grants

Each entry is a realm role plus the admin permissions it currently holds:

```bash
curl -s -b cookies.txt "$HELIX_URL/admin/realms/$REALM/admin-roles"
```
```json
[
  { "realmId": "acme", "roleId": "8d3a79d3-40e4-4497-8543-234685e689a8", "roleName": "admin",   "permissions": ["realm-admin"] },
  { "realmId": "acme", "roleId": "9ec2e116-e56b-458d-b11d-1efbfd1c492a", "roleName": "user",    "permissions": [] },
  { "realmId": "acme", "roleId": "6c898aa8-98dc-4597-87be-9352dd5f21da", "roleName": "auditor", "permissions": ["view-clients","view-events","view-identity-providers","view-users"] }
]
```

### Read the permission catalogue

The catalogue is fixed; `permissions` values in a grant must be `key`s from this list:

```bash
curl -s -b cookies.txt "$HELIX_URL/admin/realms/$REALM/admin-roles/permissions"
```
```json
[
  { "key": "realm-admin",               "label": "Full realm administration" },
  { "key": "view-users",                "label": "View users" },
  { "key": "manage-users",              "label": "Manage users" },
  { "key": "view-clients",              "label": "View clients / applications" },
  { "key": "manage-clients",            "label": "Manage clients / applications" },
  { "key": "manage-roles",              "label": "Manage realm & client roles" },
  { "key": "view-identity-providers",   "label": "View identity providers & federation" },
  { "key": "manage-identity-providers", "label": "Manage identity providers & federation" },
  { "key": "manage-authorization",      "label": "Manage authorization (flows, scopes, claims, authz services)" },
  { "key": "manage-organizations",      "label": "Manage organizations & groups" },
  { "key": "manage-realm",              "label": "Manage realm settings, notifications & provisioning" },
  { "key": "view-events",               "label": "View events & sessions" },
  { "key": "manage-events",             "label": "Manage events config & revoke sessions" },
  { "key": "reveal-bsn",                "label": "Reveal citizen service numbers (BSN)" },
  { "key": "manage-credential-issuance", "label": "Manage credential issuance (templates, offers, issued credentials)" }
]
```

**The BSN is masked unless you hold `reveal-bsn`.** User lists and details show a citizen service number as
`*****1234`; revealing the full number (`POST /admin/realms/{realm}/users/{userId}/bsn/reveal`) needs `reveal-bsn` (or
`realm-admin`) and is audited. Which applications receive the BSN is a `realm-admin` decision — see
[DigiD](../federation/digid.md#releasing-the-bsn-to-an-application).

**Credential issuance.** `manage-credential-issuance` covers [credential issuance](../federation/credential-issuance.md) templates, offers and issued credentials (revoke, suspend). Issuer key writes, the issuer settings and a template's BSN grant still need `realm-admin`. Writing a [verified-claims policy](../integration/verified-claims.md) or [verification API](../integration/verification-api.md) settings also needs `realm-admin`.

**Realm signing keys need `realm-admin`.** Whoever can import or activate a realm signing key can issue any token of
the realm, including tokens for an admin service account. So every write under `/admin/realms/{realm}/keys`
(import, CSR, certificate upload, activate, rotate, retire), and changing the realm's signing settings
(`PUT /admin/realms/{realm}/settings/signing`), requires `realm-admin`; `manage-realm` can still read both. The console hides the key actions from admins without `realm-admin`.

**No admin can grant permissions they do not hold.** Assigning or removing a role — to a user, a group, a group's
members, a service account, through realm import or SCIM — needs the role's admin permissions to be your own. The
`admin` role (and any role granting `realm-admin`) needs `realm-admin`. Changing a role's admin permissions needs
`realm-admin`. A role with admin permissions cannot be the realm's default role, and the default role cannot be given
admin permissions; a realm that already has such a default role is reported at start-up and new users no longer get it.

**You cannot act on an admin more privileged than you.** Impersonating a user, resetting their password or
credentials, changing their profile, e-mail, username, required actions or status, erasing or deleting them — and
revealing, setting or rotating a client's secret, changing its authentication, keys, redirect URIs or grant types, or
deleting it — needs the target's admin permissions (a client's: its service account's) to be your own. Revoking a
user's sessions stays allowed. Revealing a client secret needs `manage-clients`. Logins through an external identity
provider are never linked automatically (by e-mail) to an administrator: the administrator links the identity from
the account console while signed in. Binding a workload-identity issuer or an agent to a client counts as acting on
that client. On the built-in sign-in clients (`helix-console`, `helix-cli`), every change except the name, description and
"always display in console" — including their roles and mappers — needs `realm-admin`. When permissions are
compared, `manage-users`, `manage-clients`, `manage-identity-providers` and `manage-events` include their `view-`
counterparts.

**What administrators sign in through is protected.** Changing or deleting an identity provider that administrators
are linked to needs their admin permissions, and so does changing a trusted list that their wallet providers use. An
administrator signs in through a federated link only when it is trusted. Trusted links are those that existed before
1.0.0-rc.12 (reported at start-up), the sign-in that created their account, a link they made from the account console
(Connected accounts → Link, after signing in again), wallet linking, or a link a realm admin confirmed. A link made by
matching a verified e-mail address never signs in an administrator. Under **Users → (user) → Linked identities**, a
realm admin can confirm a link or revoke trust in it. The following need `realm-admin`:

- creating a client named `helix-console` or `helix-cli`;
- changing the flow, subject claim or enabled flag of their Application, or deleting it;
- the realm's `browser` flow and the flows bound to the sign-in clients;
- messaging providers and message templates;
- the MFA policy, magic links and the subject claim;
- the realm settings other than the display name, the legacy branding fields and self-registration.

Registering a push device for a user needs that user's admin permissions. Deleting a role or a group, or moving a group
under another parent, needs the admin permissions it grants. The same permission is a route requirement too: for
example, `manage-clients` can list clients. `helix-cli` is created in every realm. On every start, a `helix-cli` that
allows more than the built-in client is corrected: non-loopback redirect URIs, extra grant types, a secret, or a
confidential setting. A correction is logged and audited as `SIGN_IN_CLIENT_RECONCILED`; narrower choices are kept.

**Unknown admin routes fail closed.** An `/admin` route the permission catalogue does not name requires `realm-admin`.
Admin paths are matched on the decoded path, and a percent-encoded character in the realm id or the route name (for
example `k%65ys` instead of `keys`) is refused with `400`.

`view-identity-providers` unlocks read-only access to **trusted lists** (`GET /admin/realms/{realm}/trusted-lists`) — so an auditor can see which EU trusted lists a realm trusts and their health, without being able to change anything. It does **not** unlock identity-provider configuration or secrets: reading or writing an identity provider, its verifier keys, or its wallet connection still requires `manage-identity-providers`, same as before.

### Set a role's grants

`PUT /admin-roles/{roleId}` replaces the permission set for that role. Use the `roleId` from the list above:

```bash
# Refresh CSRF after any GET, then PUT (see the auth guide for the helper)
curl -s -b cookies.txt -c cookies.txt "$HELIX_URL/admin/realms/$REALM/admin-roles" >/dev/null
CSRF=$(awk '($6=="__Host-XSRF-TOKEN"||$6=="XSRF-TOKEN")&&$7~/^r1\./{t=$7} END{print t}' cookies.txt)

# Grant the auditor role read access to users, clients, events and federation (replaces its current grants)
curl -s -b cookies.txt -X PUT \
  -H "Content-Type: application/json" \
  -H "X-XSRF-TOKEN: $CSRF" \
  -d '{"permissions":["view-users","view-clients","view-events","view-identity-providers"]}' \
  "$HELIX_URL/admin/realms/$REALM/admin-roles/6c898aa8-98dc-4597-87be-9352dd5f21da"
```

### Full endpoint set

| Method & path (under `/admin/realms/{realm}`) | Purpose |
| --- | --- |
| `GET /admin-roles` | List realm roles with their admin-permission grants |
| `GET /admin-roles/permissions` | Read the fixed permission catalogue |
| `PUT /admin-roles/{roleId}` | Replace the admin permissions granted to a role |

Realm roles themselves are created and deleted under `/roles` — see [Roles](../manage/roles.md).

## See also

- [Roles](../manage/roles.md)
- [Security hardening](security.md)
- [Observability & health](observability.md)
- [Realm settings](realm-settings.md)
