> ## Documentation Index
> Fetch the complete documentation index at: https://radarhq.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# IdP group-based access

> Reuse the groups you already manage in your identity provider to grant Kubernetes access through Radar Cloud. Your IdP group memberships arrive as radar:idp:<group-id> subjects you bind in your own RBAC.

Most teams already express "who can touch what" as groups in their identity provider (Entra, Okta, a generic OIDC / SAML IdP). Radar Cloud lets you reuse those exact groups to grant Kubernetes access - without re-authoring per-user bindings. Your IdP group memberships flow through sign-in and land in your cluster as `radar:idp:<group-id>` subjects, which you bind in your own Kubernetes RBAC exactly as you already bind your raw IdP groups.

This page covers the end-to-end process. It builds on two pages you should read first:

* [SSO](/docs/cloud/sso) - group passthrough rides on top of a working SSO connection.
* [RBAC under Cloud mode](/docs/cloud/rbac) - the binding recipes and governance models you'll use in the final step.

## The identity Radar presents to your cluster

When a signed-in user drives a cluster request, the control plane does **not** forward the user's raw IdP identity (their IdP email, their real IdP group names) to your cluster. It asserts a **Radar-scoped, prefixed identity** that you opt into through your own RBAC. On each request the control plane injects a groups header containing:

| Subject                | Meaning                                                                      |
| ---------------------- | ---------------------------------------------------------------------------- |
| `radar:<role>`         | The user's Cloud role tier - `owner` / `member` / `viewer`.                  |
| `radar:org:<org-id>`   | The organization the identity belongs to.                                    |
| `radar:user:<user-id>` | An opaque per-user ID (stable across email changes).                         |
| `radar:email:<email>`  | The user's lowercased email, for RBAC that binds individual humans by email. |
| `radar:idp:<group-id>` | One entry per IdP group the user belongs to (this page).                     |

<Note>
  `radar:*` is the vocabulary to bind against. A legacy `cloud:*` copy of the role / org / user subjects is still emitted so older bindings keep working, but IdP groups and the email subject exist **only** under `radar:*`. Write new bindings against `radar:*`.
</Note>

**Why a prefixed identity instead of your raw one?** You likely have many existing Kubernetes RoleBindings written over years and keyed to your real IdP identity - some for `kubectl`, some for CI, some binding a platform admin to `cluster-admin`. If Radar asserted the raw identity, every one of those bindings would become reachable the moment someone signs into Radar. With the prefixed identity, only the bindings you **explicitly** grant to a `radar:*` subject are reachable. You can `grep radar:` across your RBAC and see the complete, auditable list of what Radar can touch.

This is **channel isolation** - a smaller, deliberately scoped, auditable door - not a blast-radius cap on a control-plane compromise. State it that way to a security team: the value is that Radar can reach only what you bound to `radar:*`, and that set is greppable and reviewable.

## The process end to end

1. **Connect SSO.** Group passthrough requires a working SSO connection ([SSO](/docs/cloud/sso)).
2. **Emit a stable group claim from your IdP** so memberships arrive as `radar:idp:<group-id>`.
3. **Verify** the groups arrive as stable IDs before you write any RBAC.
4. **Bind `radar:idp:<group-id>` in your Kubernetes RBAC**, optionally disabling the default role tiers for scoped users.

### 1. Connect SSO

Follow [SSO](/docs/cloud/sso) end to end. Group passthrough is enabled per connection as part of SSO onboarding; if groups don't arrive after you complete the steps below, contact support to confirm passthrough is active on your connection.

### 2. Emit a stable group claim from your IdP

This is two actions: your IdP has to **emit** a groups claim, and the SSO setup has to **map** that claim into Radar. Both are required - a claim your IdP emits but no one maps never reaches your cluster.

1. **In your IdP**, add a **groups claim** to the assertion / token it sends (see the provider examples below).
2. **In the SSO setup portal** (the "Open SSO setup" link from your [SSO](/docs/cloud/sso) connection), open **Attribute mappings → Edit attribute mappings**, find the **`groups`** attribute, and set its IdP field name to the exact attribute name your IdP emits the claim under. Save.

<Warning>
  The `groups` mapping is **optional** in the setup portal - unlike email / name it has no "Required" badge, so it is easy to skip, and if you skip it the field stays unset and **no groups ever flow** (with no error anywhere). If group access "just doesn't work," this unset mapping is the first thing to check.

  Enter the IdP field name **exactly** as your IdP emits it. It is often a bare attribute name, not a namespaced URI - e.g. Entra emits its customized group claim as plain `groups`, not `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/groups`. A near-miss maps nothing.
</Warning>

<Warning>
  **The group value must be a stable, immutable identifier - a group object ID / GUID, not a display name.** Two reasons:

  1. A RoleBinding names the value verbatim. If your IdP emits display names, renaming a group in the IdP silently breaks - or worse, repoints - every binding that named the old string.
  2. As a safety measure, Radar **drops** any group value containing whitespace, a comma, or a non-ASCII byte (this prevents a crafted group name from injecting an extra subject into the identity). A human-readable name like `Platform Admins` has a space and is dropped entirely; only a clean identifier such as a 36-character GUID survives.

  Configure your IdP to source the groups claim from the immutable group ID. This is usually the default.
</Warning>

Provider examples (illustrative - any OIDC / SAML IdP works the same way):

* **Microsoft Entra:** add a groups claim (App registration → Token configuration, or Enterprise App → Single sign-on → Attributes & Claims). Leave the **source attribute at its default** so the value is the group **object ID (GUID)**, not `sAMAccountName` or the cloud display name.
* **Okta:** emit a groups claim on the SAML / OIDC app and use the **immutable group ID** as the value, not the group label.
* **Generic OIDC / SAML:** expose your IdP's groups claim and source it from a **stable ID**.

### 3. Verify before writing RBAC

Before you author a single RoleBinding, confirm your IdP actually sends the groups - as stable IDs. You can check this yourself in the SSO setup portal, no support ticket needed:

1. In the setup portal, click **Test sign-in** (or just have a user who belongs to the group sign in normally).
2. Open the **Sessions** list, click the sign-in you just made, and expand the **SAML response** (SAML) or token payload.
3. Confirm a groups attribute is present and its values are stable IDs (e.g. `a1b2c3d4-1234-5678-9abc-def012345678`), **not** display names with spaces.

If the groups attribute is **absent**, your IdP isn't emitting it or the attribute mapping is unset (step 2). If it's present but the values are **display names**, they'll be dropped and the user will see nothing - fix the IdP source attribute so it emits the group object ID. Only once the assertion carries the expected IDs should you write bindings whose subject is `kind: Group, name: radar:idp:<group-id>`.

### 4. Bind the groups in Kubernetes RBAC

You now bind `radar:idp:<group-id>` subjects in **your own** Roles and RoleBindings, exactly as you bind your raw IdP groups today. A minimal example - a vendor group that can edit one namespace:

```yaml theme={null}
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: vendor-team-edit
  namespace: vendor-prod
subjects:
  - kind: Group
    name: radar:idp:a1b2c3d4-1234-5678-9abc-def012345678   # the group's object ID
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: edit
  apiGroup: rbac.authorization.k8s.io
```

Two related subjects are available for cases where a group isn't the right key:

* `radar:email:<lowercased-email>` - bind an individual human by email, for RBAC that grants specific people rather than groups.
* `radar:user:<user-id>` - bind by opaque, email-independent user ID (see [Per-user bindings](/docs/cloud/rbac#per-user-bindings)).

**Scoped users should usually get only what their group grants.** By default every Cloud user also receives a role-tier binding (`radar:viewer` → `view`, etc.). For external or namespace-scoped users who should have *no* baseline access on top of their group, disable the default viewer tier so they start from nothing:

```yaml theme={null}
cloud:
  defaultRbac:
    viewer: false
```

The two paths compose: keep the default tiers for your internal operators, and grant scoped external users access exclusively through their `radar:idp:<group-id>` bindings. The [RBAC governance models](/docs/cloud/rbac#choosing-a-governance-model) walk through the mixed and full-IdP setups in depth - this page is the "how the groups get there" half of that.

## How enforcement works

Kubernetes RBAC is the single policy source; there is no second permission system to configure. But **where** the check happens differs by surface, and the distinction matters:

* **Direct resource reads and writes** (get / list / watch of pods, nodes, etc.; create / update / delete; exec / logs / attach) are served by an **impersonating** client: Radar calls the Kubernetes API *as* your asserted identity, and **Kubernetes itself** allows or denies. The API server is the enforcement point.
* **Cached surfaces** - the timeline / change feeds and the topology / neighborhood graph - are served from a shared cache rather than per-user live calls. For these, Radar asks Kubernetes "is this user allowed to see this kind here?" via a `SubjectAccessReview`, then filters the cached data against that answer.

Short version: **Kubernetes RBAC is the rulebook; Kubernetes enforces direct reads and writes, and Radar filters cached reads against the same rulebook.**

<Note>
  Full impersonation enforcement requires the Radar Helm chart at the version that enables cloud-mode (see [Connecting a cluster](/docs/cloud/connect-cluster)). On older charts you get header injection only, and the in-cluster ServiceAccount still calls with its own ambient permissions - so tier and group bindings don't actually enforce until the chart is rolled.
</Note>

## Troubleshooting

**A user signs in fine but sees nothing.** Almost always the IdP is emitting group **display names instead of stable IDs**. Names that contain spaces (or commas / non-ASCII characters) are dropped, so no `radar:idp:*` subject reaches the cluster and nothing is bound. Point the group claim at the immutable group object ID and have the user sign in again. Secondary causes: no RoleBinding yet names that group's ID, or group passthrough isn't enabled on the connection (contact support).

**Renaming a group in the IdP broke access.** You bound a display name rather than the object ID, and the rename changed the string your RoleBinding referenced. Rebind to the group's immutable ID; renames won't affect it.

**A group with a clean ID still grants nothing.** Confirm the RoleBinding subject is exactly `radar:idp:<group-id>` (the `radar:idp:` prefix is required - the raw group ID alone won't match) and that the ID in the binding matches the ID your IdP emits, character for character.

## See also

* [SSO](/docs/cloud/sso) - the connection this builds on.
* [RBAC under Cloud mode](/docs/cloud/rbac) - binding recipes, the default role tiers, and the mixed vs full-IdP governance models.
* [Organizations & roles](/docs/cloud/organizations) - the Cloud roles that seed the default tiers.
* [Connecting a cluster](/docs/cloud/connect-cluster) - the chart values that enable cloud-mode enforcement.
