docs: add Entra ID SSO and group mapping guide (#1635)

Expands the Azure AD provider example into a full Microsoft Entra ID walkthrough
contributed by @drgimpfen in #1635: app registration (single tenant), Web
platform redirect URI, Token configuration groups claim as Group ID, Enterprise
Application "Assignment required = Yes", and mapping roles from the group Object
ID so a rename in Entra doesn't break admin access. Corrects the contributed
draft to use the provider-scoped v6 callback path
(/api/oidc/<provider-id>/callback, not the legacy v5 /api/oidc/callback) and
adds the issuer URL and client ID/secret steps the draft omitted. Keeps the
draft's AADSTS650053 finding: Entra has no `groups` scope, so groups must come
from the ID token claim rather than the scope list.

Adds a warning the draft did not cover: on group overage Entra drops the groups
claim for a _claim_names/_claim_sources marker that Pulse does not follow, and
since a configured mapping is authoritative that login clears the user's roles.

Three pre-existing doc defects fixed alongside:

- OIDC.md claimed there is no environment-variable override for group role
  mappings, contradicting CONFIGURATION.md and RBAC.md.
  OIDC_GROUP_ROLE_MAPPINGS does exist (internal/config/oidc.go), it just only
  feeds the legacy env-configured provider.
- RBAC.md claimed logins with zero matching groups do not clear existing role
  assignments. applySSORoleAssignments runs with mappingAuthoritative=true
  whenever the provider has any mappings, so it calls UpdateUserRoles with the
  empty set and does clear them.
- Two RBAC.md links pointed at OIDC.md#group-to-role-mapping-pro, but the
  heading renders as #group-to-role-mapping-pro-and-above.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
courtmanr@gmail.com 2026-07-28 10:43:27 +01:00
parent 1dc19bfec0
commit 48c269f58d
2 changed files with 39 additions and 11 deletions

View file

@ -1,6 +1,6 @@
# 🔐 OIDC Single Sign-On
Enable Single Sign-On (SSO) with providers like Authentik, Keycloak, Okta, and Azure AD.
Enable Single Sign-On (SSO) with providers like Authentik, Keycloak, Okta, and Microsoft Entra ID.
## 🚀 Quick Start
@ -40,9 +40,9 @@ Automatically assign Pulse roles based on OIDC group membership. When a user log
**Configuration:**
Group-role mappings are configured per SSO provider through the UI (or the
SSO provider API for automated setup) — there is no environment-variable
override. Go to **Settings → Security → Single Sign-On**, edit the provider,
and populate **Group Role Mappings** with entries like:
SSO provider API for automated setup). Go to **Settings → Security → Single
Sign-On**, edit the provider, and populate **Group Role Mappings** with
entries like:
- `oidc-admins` → `admin`
- `oidc-operators` → `operator`
- `oidc-viewers` → `viewer`
@ -51,6 +51,11 @@ The mappings persist on the provider record as a `groupRoleMappings` JSON
field. Provider-level config (including this field) can be PUT through the
SSO provider API for automated setup.
`OIDC_GROUP_ROLE_MAPPINGS` populates the same field, but only for the legacy
single-provider OIDC configuration built from `OIDC_*` environment variables —
it has no effect on providers created through the UI or the SSO provider API.
See [CONFIGURATION.md](CONFIGURATION.md).
**How it works:**
- On each login, Pulse reads the user's groups from the configured groups claim.
- For each group that matches a mapping, the corresponding role is assigned.
@ -98,10 +103,33 @@ For persistent sessions that don't require frequent re-authentication:
- **Valid Redirect URIs**: `https://pulse.example.com/api/oidc/<provider-id>/callback`
- **Issuer URL**: `https://keycloak.example.com/realms/myrealm`
### Azure AD
- **Redirect URI**: `https://pulse.example.com/api/oidc/<provider-id>/callback` (Web)
### Microsoft Entra ID (formerly Azure AD)
Create the provider in Pulse first (**Settings → Security → Single Sign-On → Add Provider**) so it gets its ID — Pulse generates a UUID per provider and shows the resulting callback URL. You need that URL for the Entra redirect URI below.
**In the Entra admin center:**
1. **App registrations → New registration**: give it a name (e.g. `Pulse SSO`) and choose *Accounts in this organizational directory only (Single tenant)*.
2. **Authentication → Add a platform → Web**: set the redirect URI to `https://pulse.example.com/api/oidc/<provider-id>/callback` and tick **ID tokens**.
3. **Certificates & secrets → New client secret**: copy the secret **Value** (not the Secret ID) — it is only shown once.
4. **Token configuration → Add groups claim**: select **Groups assigned to the application**, expand **ID**, and choose **Group ID**.
5. **Enterprise applications → (your app) → Properties**: set **Assignment required?** to `Yes`, so only assigned users and groups can sign in.
6. **Enterprise applications → (your app) → Users and groups**: assign the security group (e.g. `Pulse-Admins`) and copy its **Object ID** — a GUID like `a1b2c3d4-e5f6-7890-abcd-123456789abc`.
**In Pulse:**
- **Issuer URL**: `https://login.microsoftonline.com/<tenant-id>/v2.0`
- **Note**: Enable "ID tokens" in Authentication settings.
- **Client ID**: the app registration's *Application (client) ID*.
- **Client Secret**: the secret value from step 3.
- **Redirect URI**: `https://pulse.example.com/api/oidc/<provider-id>/callback`, matching the app registration exactly. The bare `/api/oidc/callback` is a v5 compatibility path that only serves the legacy env-configured provider — don't use it for a new provider.
- **Scopes**: exactly `openid profile email`. Do **not** add `groups`. Entra has no `groups` scope and fails the whole authorization request with `AADSTS650053`; group membership arrives in the ID token from the Token configuration step, not from a scope.
- **Groups Claim**: `groups`
- **Allowed Groups**: the group's Object ID (GUID), not its display name.
- **Group Role Mappings**: `<guid>=admin`. Keying on the Object ID means the mapping survives a group rename in Entra.
> **Warning — group overage**: if a user belongs to more groups than Entra will fit in a token, Entra omits the `groups` claim entirely and sends a `_claim_names` / `_claim_sources` overage marker pointing at Microsoft Graph instead. Pulse does not follow that marker, so it sees the user as having no groups — and because a configured group-role mapping is authoritative, that login **clears** the user's role assignments instead of leaving them alone. Selecting **Groups assigned to the application** rather than **Security groups** in Token configuration keeps the claim small and avoids the overage.
> **Note**: Group-to-role mapping requires a Pro (or above) license. Plain SSO login and **Allowed Groups** gating work on any plan.
## 🔧 Troubleshooting

View file

@ -25,7 +25,7 @@ Users can hold multiple roles. Their effective permissions are combined across a
### OIDC Group Mapping
When using OIDC/SSO, roles can be automatically assigned based on group membership. See [OIDC Group-to-Role Mapping](OIDC.md#group-to-role-mapping-pro) for configuration.
When using OIDC/SSO, roles can be automatically assigned based on group membership. See [OIDC Group-to-Role Mapping](OIDC.md#group-to-role-mapping-pro-and-above) for configuration.
---
@ -134,7 +134,7 @@ If you use an OIDC identity provider, Pulse can automatically assign roles based
**UI:** Settings → Security → Single Sign-On → Group Role Mappings
**Environment variable:**
**Environment variable** (legacy env-configured OIDC provider only — it does not apply to providers created through the UI or the SSO provider API):
```bash
# Format: group1=role1,group2=role2
OIDC_GROUP_ROLE_MAPPINGS="oidc-admins=admin,oidc-operators=operator,oidc-viewers=viewer"
@ -144,10 +144,10 @@ How it works:
- On each login, Pulse reads the user's groups from the OIDC groups claim.
- Matching groups are mapped to Pulse roles.
- A user can receive multiple roles from multiple group mappings.
- When at least one mapped role is found, role assignments are updated to match. Note: logins with zero matching groups do not clear existing role assignments.
- Once a provider has any group role mappings configured, the mapping is authoritative: on every login the user's role assignments are replaced with whatever the mapping resolves to. A login that matches no mapped group resolves to an empty set, which clears the user's existing role assignments. Watch for identity providers that silently drop the groups claim (see the Entra ID group overage warning in [OIDC.md](OIDC.md#microsoft-entra-id-formerly-azure-ad)) — to Pulse that looks the same as losing every group.
- Role changes are logged to the [audit log](AUDIT_LOGGING.md) as `oidc_role_assignment` events.
See [OIDC documentation](OIDC.md#group-to-role-mapping-pro) for full configuration details.
See [OIDC documentation](OIDC.md#group-to-role-mapping-pro-and-above) for full configuration details.
---