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>
RBAC.md (alerts:read → monitoring:read):
The example team-setup table told operators to issue API tokens with an
"alerts:read" scope. That scope does not exist in pkg/auth/scopes.go;
defined scopes are monitoring:read, settings:read, etc. /api/alerts/ is
gated by RequireAuth (no specific scope required), so an integrator
issuing a token would naturally pick the closest real scope —
monitoring:read — and that is what the doc should have shown.
OIDC.md (OIDC_GROUP_ROLE_MAPPINGS, OIDC_CA_BUNDLE):
Both env vars were documented but zero code reads them. OIDC config is
per-provider in internal/config/sso.go and OIDCProviderConfig in
internal/config/oidc.go: groupRoleMappings is a map field; caBundle is a
path field. Replace both env-var snippets with the actual UI/API path so
operators following the secure-install flow don't silently get no group
mapping or no custom CA trust. Same drift pattern as the earlier rc.1 →
rc.5 PULSE_RELAY_* aspiration-without-implementation.
WEBHOOKS.md (missing helpers):
notifications.go's templateFuncMap registers jsonString and pathescape
on every webhook template, but the helper list only documented title /
upper / lower / printf / urlquery / urlencode / urlpath. Add both, with
a short note that jsonString is the safe way to embed arbitrary string
values inside a JSON payload — Pulse's shipped templates use it
everywhere a value goes inside JSON, and operators writing custom
templates were missing the canonical escape primitive.
KUBERNETES.md (helm path + markdown fence):
- "deployment.strategy.type=Recreate" was the wrong helm path. The
chart's strategy block is at the top level (deploy/helm/pulse/values.yaml
line 9), so `strategy.type=Recreate` is what operators must actually
--set. Following the broken path produced no override and left RWO
PVC deployments on the default RollingUpdate, the exact Multi-Attach
failure mode the note was trying to warn against.
- Trailing ```text on the helm-template code block closed the fence
but tagged it as a language, breaking markdown rendering in some
readers. Reduced to plain ```.
All four are doc-only changes; no code reads the names they document.