Skip to content

Commit cd43416

Browse files
docs(permissions): the SSO page describes how IdP groups grant permission sets (#31664)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9NeNpMcmXafqTuBdcSgKZYa)_ Fixes #31658 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:SSO 文档页新增一节「Grant permission sets from IdP groups」,讲清 SSO 登录时 IdP 组如何按映射授予普通权限集;SCIM 凭据一节加一行回链(同一映射模型,两个来源)。 - 为什么改:#31657 已上线 SSO 一半的组映射行为,运维文档还没写,管理员找不到怎么配。 - 风险与回滚:纯文档,不发布任何包;回滚即 revert 本 PR。 - 先看:`content/docs/permissions/sso.mdx`(新节,逐条对照 #31657 的代码与 changeset)、`content/docs/permissions/authentication.mdx`(一行回链)。 - 两分钟验证:`pnpm check:doc-anchors`,预期两处新锚点链接都能解析、全绿。 - 需要你做的:无,席位落地。 ## What changes `content/docs/permissions/sso.mdx` gains one section, **Grant permission sets from IdP groups**, describing the SSO half of IdP group mapping that PR #31657 landed. Every statement was read from the merged code (`sso-group-projection.ts`, `idp-group-mapping-admin.ts`, the route-ledger notes) and #31657's changeset, not written from memory: - the groups claim is configured in the provider's own `mapping.extraFields.groups` (on `oidcConfig.mapping` / `samlConfig.mapping` at `POST /api/v1/auth/sso/register`); a string or a list; a provider whose mapping fills no `groups` signs in as before; - a mapping is saved through `POST /api/v1/auth/admin/scim/set-group-mapping` with `providerId` (exactly one of `connectionId` / `providerId`; exactly one of `groupExternalId` / `groupDisplayName`), removed through `delete-group-mapping`; - only an ordinary permission set inside the provider's organization is granted; the save refusals (`400 VALIDATION_FAILED` on `providerId` / `permissionSet`, `503` with SSO off or no security service); - grants follow the IdP at every sign-in, a sign-in with no groups claim removes that provider's own earlier grants, and a grant made by hand or through SCIM is never touched; - a failure is logged at `error` and never fails the sign-in. The section links the SCIM credentials section of `content/docs/permissions/authentication.mdx`, and that section gains one back-link line (one mapping model, two sources). Rules only; no bypass narrative. ## Tests Docs only: no test, no ablation (os-dev: docs are not ablated). The derived doc gates below are the verification. ## Acceptance notes - **No SCIM group-mapping section exists to link.** The delta asked to link "the SCIM page's group-mapping section"; on `origin/main` no page under `content/docs/` describes the SCIM group mapping that PR #31443 landed (zero hits for `set-group-mapping`, `sys_idp_group_mapping`, `groupDisplayName` outside this diff). The link and back-link therefore use the existing **SCIM Connection Credentials** section, and the new SSO section carries the shared door's rules for both sources. Documenting the SCIM half's push-time behaviour is left out of this card's file surface. - **ADR-0152 is not linked**: it is not on `origin/main` (PR #31636 still open). - **The no-code Setup form cannot set the groups claim.** `register-sso-provider.ts` writes `oidcConfig.mapping` as `{ email, name }` only and no SAML mapping, so the page sends operators to the vendor's `/sso/register` for `extraFields`. Noted, not filed. - **Changeset:** `skip-changeset`. No package's `files[]` ships `content/docs/` (grep of every `package.json` under `packages/` and `apps/`), so the diff publishes nothing. ## Verification Head: `a7bcac5c` (origin/main `8c9260f7`, which carries PR #31657, merged in; no rebase). <details><summary>Derived gates on the head</summary> `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on `a7bcac5c` derived 45 commands (the same 45 as on the pre-merge head `af648c0e`); all 45 ran with exit 0 after the spec, lint and client-react closure was rebuilt for the merged tree, and `--ran` reconciled: 45 derived, 45 run, 0 NOT-MEASURED, 0 UNRUN. Among them `check:doc-anchors` (956 internal fragment links resolve, the two new ones included), `check:doc-authoring`, `check:doc-frontmatter`, `check:docs-single-h1`, `check:doc-route-spelling` (advisory, population clean) and `check:doc-security-posture`. On the pre-merge head, `check:skill-examples` first exited 3 (PREREQUISITE NOT MET: `client-react` had no `.d.ts`); it ran green after that package was built, and green again on this head. </details> <details><summary>Lint</summary> No package is touched (two `.mdx` pages only), so no package-scoped `pnpm lint` applies. </details> <details><summary>Merged behaviour read</summary> `sso-group-projection.ts`, `idp-group-mapping-admin.ts`, `scim-group-projection.ts`, `auth-manager-plugin-list.ts` and #31657's changeset are byte-identical between #31657's last head `70db6d06` and `origin/main` `8c9260f7` (`git diff --stat` empty), so the page describes what landed. </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmSWdZY3vu8awytLsrR)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent a2d0f73 commit cd43416

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

‎content/docs/permissions/authentication.mdx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,10 @@ the identity provider to it, then revoke the old one; both authenticate until
12031203
you do. Each mint and each revoke writes one `sys_audit_log` row naming the
12041204
administrator and the connection, never the credential.
12051205

1206+
A connection's groups grant permission sets through the same group mappings an
1207+
SSO provider's sign-in uses; see
1208+
[Grant permission sets from IdP groups](/docs/permissions/sso#grant-permission-sets-from-idp-groups).
1209+
12061210
---
12071211

12081212
## Client Integration

‎content/docs/permissions/sso.mdx‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,64 @@ before this rule, turn on `allowJit` for that provider.
426426

427427
---
428428

429+
## Grant permission sets from IdP groups
430+
431+
An SSO provider, OIDC or SAML, can decide what its users may do from the groups
432+
the identity provider sends at sign-in. It uses the same group mappings
433+
(`sys_idp_group_mapping`) as a
434+
[SCIM connection](/docs/permissions/authentication#scim-connection-credentials):
435+
one mapping model, two sources.
436+
437+
**Where the groups come from.** Set the provider's own claim mapping to fill
438+
`groups`: `mapping.extraFields.groups` names the OIDC claim or the SAML attribute
439+
that carries the user's groups, for example `extraFields: { groups: 'groups' }`
440+
in `oidcConfig.mapping` (or `samlConfig.mapping`) when the provider is registered
441+
through `POST /api/v1/auth/sso/register`. The **Setup → SSO Providers** form does
442+
not set it. The value may be one string or a list of strings. A provider whose
443+
mapping fills no `groups` signs users in as before.
444+
445+
**Saving a mapping.** A platform administrator saves one mapping per group and
446+
permission set through `POST /api/v1/auth/admin/scim/set-group-mapping`, naming
447+
the provider with `providerId`:
448+
449+
```bash
450+
curl -X POST http://localhost:3000/api/v1/auth/admin/scim/set-group-mapping \
451+
-H 'Content-Type: application/json' -H "Authorization: Bearer $ADMIN_SESSION" \
452+
-d '{ "providerId": "okta", "groupDisplayName": "Sales", "permissionSet": "sales_rep" }'
453+
# → { success, data: { mappingId, created, providerId, permissionSet, organizationId, reprojected: false, … } }
454+
```
455+
456+
- Name the source with exactly one of `connectionId` (a SCIM connection) or
457+
`providerId` (an SSO provider). Name the group with exactly one of
458+
`groupExternalId` (matched against the group's id, such as an Entra ID group
459+
object id) or `groupDisplayName` (matched against its exact name).
460+
- The grant's organization is the provider's own organization. An unknown
461+
provider, or one that belongs to no organization, is refused
462+
`400 VALIDATION_FAILED` on `providerId`. With SSO off the save is refused `503`.
463+
- Only an ordinary permission set inside that organization can be granted. An
464+
administrator set, or any set outside the organization boundary, is refused
465+
`400 VALIDATION_FAILED` on `permissionSet`, and nothing is stored. With no
466+
security service to judge the boundary, the save is refused `503`.
467+
- `POST /api/v1/auth/admin/scim/delete-group-mapping` with `{ "mappingId": "…" }`
468+
removes a mapping, and at once the grants of that set its provider made in
469+
that organization, unless another of the provider's mappings still names the
470+
set.
471+
472+
**At every sign-in.** The user's grants follow the IdP's groups. A mapped group
473+
grants its set in the provider's organization, and a group the user has left
474+
loses its set at their next sign-in. A sign-in with no groups claim removes the
475+
grants that provider made earlier. The boundary is judged again before every
476+
grant. Only grants the provider itself made are ever removed: a grant made by
477+
hand or through SCIM is never touched. A saved mapping applies at each member's
478+
next sign-in, because a provider sends groups only when someone signs in; use
479+
SCIM when a group change must take effect between sign-ins.
480+
481+
**When applying the mappings fails**, the sign-in still succeeds and the failure
482+
is logged at `error`. The user keeps the grants of their previous sign-in until
483+
the next one applies the mappings again.
484+
485+
---
486+
429487
## Verify configuration
430488

431489
Restart the server (`pnpm dev`), then:

0 commit comments

Comments
 (0)