Skip to content

plugin-auth: a SCIM group push grants nothing — no projection maps a group to a role or permission set, though last-admin-guard counts it a live write shape #30959

Open

Description

@objectstack-fleet

repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2

Filing gate: (b) a contract gap with a named consumer, measured by the cloud#3667 dev on a real licensed objectos-ee serve (report on objectstack-ai/cloud#3667, Enterprise SCIM leaver lifecycle; draft PR objectstack-ai/cloud#3672). Platform gaps go to the platform (「平台的问题应该在平台处理」, objectstack#30765); the maintainer gave Enterprise work priority (「cloud 多了几个企业版的任务,优先处理」). Consumer: objectstack-ai/cloud#3667 (live Okta / Entra ID provisioning).

维护者速读

IdP 把用户推进某个 SCIM 组以后,平台会建组、加成员,但不会因此给这个用户任何角色或权限集。企业版要求「IdP 里的组决定平台里的权限」,现在做不到。需要平台支持把 SCIM 组映射到角色或权限集。

Measured (cloud pin 2ffdb649 on the serve; objectstack 6ddf827236 read)

  • SCIM POST /api/v1/auth/scim/v2/Groups with the user as a member → 201. The user's sys_user_permission_set rows and sys_member.role are unchanged (cloud#3672 parks this as it.skip).
  • packages/plugins/plugin-auth/src/auth-manager-plugin-list.ts:798 calls scim({ connections, authentication, identity }) with no projection, so @better-auth/scim's group membership touches nothing in the platform's grant model.
  • last-admin-guard.ts names a SCIM group-mapping change as a write shape it guards, which describes a mapping that does not exist.

Expected

  • A declared mapping from a SCIM group (by displayName or externalId, per connection or per organization; the lane decides) to an organization role and/or permission sets. Membership add and remove apply and revoke the grant.
  • Grants go through the existing assignment doors (assignable-set boundary, last-admin guard), not around them. A mapping that would drop the last admin is refused as the guard refuses other shapes.
  • Each grant change writes the audit row the assignment door already writes, attributed to the SCIM connection.

Acceptance

  • Tests: push a mapped group with a member → the set is granted; remove the member → revoked; an unmapped group grants nothing; a mapping that would remove the last admin → refused as a SCIM 403.

Suggested-grade: p1 · Enterprise priority (maintainer).

Dedupe (2026-11-06): SCIM group role mapping → 21 hits (#27314 open is the bridges epic, a different surface; the rest closed); scim projection → 47 hits, top 4 closed and unrelated. None covers this gap.

Activity

  1. added
    bugSomething isn't working
    priority:p1High: required for production / M2
    pm:queueReady for the PM dispatch loop
    repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 6, 2026
  2. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p1 kept · domain:server · 2026-11-06T14:31Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4czvZykUqhYxYXSSDt4

    Claim: PM loop round 1 · 2026-11-06T14:48Z
    Session: session_016WV4czvZykUqhYxYXSSDt4
    Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: the card thread's designated claude/project-thread-* branch, re-posted in a whole claim once the thread reports it
    Worktree: objectstack-issue-30959
    Domain: domain:server
    Seat: domain:server#4
    File surface: the SCIM wiring and a group projection in packages/plugins/plugin-auth/src/** (auth-manager-plugin-list.ts, scim-connection-service.ts, a new projection module, last-admin-guard.ts only where it names the SCIM mapping shape); the existing role and permission-set assignment doors it calls (read, or a caller face exposed with no new judgment); their tests and dogfood; content/docs/** sentences on SCIM provisioning; one root .changeset/ file. ⛔ packages/spec/**: if the mapping needs a published key, schema or settings key, the dev stops after measuring and the seat takes the design choice to the maintainer and files the domain:spec sub-card. ⛔ ui/**, skills/** (stop on breach; explain in the report)
    Container & model: L, mode:thread, model: default, effort: xhigh (p1)
    Clause-②: yes (widening) (provisional: a SCIM group push starts granting roles and permission sets; the dev measures and reports the arm)
    Metadata-protocol: none (provisional; added only through the spec sub-card)
    Responsibility: n/a — capability gap with a named consumer (objectstack-ai/cloud#3667), not a regression
    Thread-read: 6687271791
    Premise: card body (Enterprise priority, 「cloud 多了几个企业版的任务,优先处理」) and triage 6687271791 (p1 kept).

    • A declared mapping from a SCIM group (by displayName or externalId) to an organization role and/or permission sets. Adding a member grants, removing one revokes, and an unmapped group grants nothing.
    • Every grant goes through the existing assignment doors (assignable-set boundary, last-admin guard) and writes their audit row, attributed to the SCIM connection. A mapping change that would drop the last admin is refused as a SCIM 403.
    • Whether the mapping is a spec key or a settings row is measured first and decided before any build.
      Serial constraints cleared: no open PR touches packages/plugins/plugin-auth (searched at claim); the dev re-reads open PRs on it at branch start

    Contract review owed (a widening of a security boundary: a new grant path) before the queue.

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 6, 2026
  5. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4czvZykUqhYxYXSSDt4

    Claim: PM loop round 1 (whole claim re-posted with the branch) · 2026-11-06T14:52Z
    Session: session_016WV4czvZykUqhYxYXSSDt4
    Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/project-thread-qta161
    Dev session: session_016WV4cwCnS3MmnAfNkXm8MS (card thread)
    Worktree: objectstack-issue-30959
    Domain: domain:server
    Seat: domain:server#4
    File surface: the SCIM wiring and a group projection in packages/plugins/plugin-auth/src/** (auth-manager-plugin-list.ts, scim-connection-service.ts, a new projection module, last-admin-guard.ts only where it names the SCIM mapping shape); the existing role and permission-set assignment doors it calls (read, or a caller face exposed with no new judgment); their tests and dogfood; content/docs/** sentences on SCIM provisioning; one root .changeset/ file. ⛔ packages/spec/**: if the mapping needs a published key, schema or settings key, the dev stops after measuring and the seat takes the design choice to the maintainer and files the domain:spec sub-card. ⛔ ui/**, skills/** (stop on breach; explain in the report)
    Container & model: L, mode:thread, model: default, effort: xhigh (p1)
    Clause-②: yes (widening) (provisional: a SCIM group push starts granting roles and permission sets; the dev measures and reports the arm)
    Metadata-protocol: none (provisional; added only through the spec sub-card)
    Responsibility: n/a — capability gap with a named consumer (objectstack-ai/cloud#3667), not a regression
    Thread-read: 6687271791
    Premise: card body (Enterprise priority, 「cloud 多了几个企业版的任务,优先处理」) and triage 6687271791 (p1 kept).

    • A declared mapping from a SCIM group (by displayName or externalId) to an organization role and/or permission sets. Adding a member grants, removing one revokes, and an unmapped group grants nothing.
    • Every grant goes through the existing assignment doors (assignable-set boundary, last-admin guard) and writes their audit row, attributed to the SCIM connection. A mapping change that would drop the last admin is refused as a SCIM 403.
    • Whether the mapping is a spec key or a settings row is measured first and decided before any build.
      Serial constraints cleared: no open PR touches packages/plugins/plugin-auth (searched at claim); the dev re-reads open PRs on it at branch start

    Contract review owed (a widening of a security boundary: a new grant path) before the queue.

  6. added
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 6, 2026
  7. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4czvZykUqhYxYXSSDt4

    Parked · 2026-11-06T15:06Z · section 1 measured (/mnt/project-files/server-lane-seat4/30959/section1-measurements.md). The build is serial behind two sibling cards that edit the same files (scim-connection-service.ts, auth-manager-plugin-list.ts). This card rides #30960's SCIM write attribution (svc:scim:<connection>) instead of building its own. The storage of the mapping, and whether an IdP group may confer admin standing, are with the maintainer on a decision card. ADR-0134 D4 ("no group→role mapping in V1") needs an amendment, so the PR will be Tier H. The claim (6687734014) and the card thread stand.

    Blocked-by: #30960
    Blocked-by: #30958

  8. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4czvZykUqhYxYXSSDt4

    Release: claim 6687734014 · 2026-11-06T16:25Z · server seat 4 is signing off on the maintainer's word, and this card has not started building. It stays pm:blocked (Blocked-by: #30960, Blocked-by: #30958). The assignee is removed, so the next seat claims it fresh.

    Handoff for that seat:

    • Section 1 is measured: /mnt/project-files/server-lane-seat4/30959/section1-measurements.md (project files; the summary is in the parked note above).
    • Settled by the seat: match a group by externalId, then displayName. Ride plugin-auth: SCIM-driven writes leave sys_audit_log rows with actor, user_id and metadata null, so no audit row names the SCIM connection that acted #30960's SCIM write attribution (svc:scim:<connection>); don't build a second one.
    • Still open, for the maintainer: where the mapping lives (recommended: a per-connection platform-object row, sets only) and whether an IdP group may confer admin standing (recommended: no in V1). The seat's decision card on this was withdrawn at sign-off, so the next seat re-asks.
    • Owed: an ADR-0134 D4 amendment ("no group→role mapping in V1"), a fable-tier surface, so the PR is Tier H. One platform-object-names constant in spec needs the spec lane's nod.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:blockedBlocked by another issue/PR — body carries Blocked-by: #Npriority:p1High: required for production / M2repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions