Skip to content

SCIM connection credentials: add a Setup action for mint and revoke #31032

Closed

Description

@objectstack-fleet

domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5

Follow-up of #30958 (PR #30991, merged at 41dc302).

Why

#30991 shipped the two platform-admin routes POST /api/v1/auth/admin/scim/mint-credential and POST /api/v1/auth/admin/scim/revoke-credential, documented as curl calls in authentication.mdx. There is no way to reach them from the console. A platform administrator setting up SCIM provisioning still has to use the command line to get a bearer token.

What

  • A type: api action on the SCIM connection credential object that calls the mint route and shows the token once in a result dialog. The token is never stored or shown again.
  • A revoke action on a credential row, calling the revoke route (it already answers 404 / 409).
  • Platform-objects translations for both actions, and an AI exposure classification for each.
  • Optional, from contract review 6689658194 (nit 4): the mint door's own audit row could carry metadata.impersonatedBy, so the row says who acted on an impersonation session. /admin/create-user's row doesn't do this either today.

Out of scope

Letting an organization administrator mint, which would widen who can mint. That is a maintainer decision and is not proposed here.

Activity

  1. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    enhancementNew feature or request
    pm:queueReady for the PM dispatch loop
    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 · p2 · domain:server · 2026-11-06T18:56Z

    ⛔ Not a claim.


    Generated by Claude Code

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

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · marchtian · session_017ihL2dFb2QdZEHEJ83cR2B

    Claim: PM loop round 1 · 2026-11-07T12:25Z
    Session: session_017ihL2dFb2QdZEHEJ83cR2B
    Account: marchtian
    Branch: claude/project-thread-6pto5z
    Worktree: objectstack-issue-31032 (the card thread's own checkout)
    Domain: domain:server
    Seat: domain:server#3
    File surface: packages/platform-objects/** (the SCIM credential/binding objects, translations via tooling); optionally packages/plugins/plugin-auth/src/scim-connection-admin.ts for the card's item 4 (stop on breach; explain in the report)
    Container & model: M, mode:thread (card thread cmsg_01VmuUhXU9SSUzqJYyXSmYSiGwrQnU7LLG4tMfKYbWXT48 of this account's project), model: TIER_DEFAULT — dispatch-gates --tier: no path-derived mandate, effort: session default — card-thread effort is set by the maintainer only; the seat's reading is high (M)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6692886264
    Premise: holds — read at origin/main 5f00374: both platform-admin routes exist in plugin-auth; sys-scim-connection-credential.object.ts carries no actions
    Serial constraints cleared: #31033 (one-line cap fix in scim-connection-admin.ts) is held behind this card, not dispatched; open PR #31013 touches other plugin-auth files (auth-manager-plugin-list.ts, auth-manager-after-hook.ts, auth-plugin.ts), disjoint; siblings #31085/#31029 (this seat) touch other platform-objects files

    The dispatch order is the card thread's opening message (files server-seat-3/orders/_standing.md + server-seat-3/orders/31032.md in this account's project); slot granted by the project coordinator.

  5. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · marchtian · session_017ihL2c3bRFyY1fQ6vWAW1Z
    os-dev-report

    {
      "issue": 31032,
      "status": "done",
      "branch": "claude/project-thread-6pto5z",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/31119",
      "session": "session_017ihL2c3bRFyY1fQ6vWAW1Z",
      "premise_still_valid": true,
      "summary": "sys_scim_connection_credential gains mint_scim_credential (list toolbar, type api, POST the mint mount, params named for the mount's body keys connectionId/label/organizationId/provisioningDomainId/expiresAt, token revealed once in a resultDialog) and revoke_scim_credential (list item + record header, POST the revoke mount, recordIdParam credentialId, confirmText, offered on active rows only). Both are offered only on current_user.isPlatformAdmin and declare PLATFORM_ADMIN_SETTINGS_GATE, the same declarations every /api/v1/auth/admin/ action carries; the mounts' gate is unchanged and no second permission path exists. AI: mint bucket H (no ai block), revoke bucket C (exposed, requiresConfirmation). Precedent for the one-shot reveal: sys_api_key.create_api_key and sys_oauth_application.create_oauth_application. Mint sits on the credential object because the mount is keyed by connectionId and writes a credential row; the binding object is never read by it. Item 4 taken: a mint on an impersonation session writes metadata.impersonated_by (plugin-audit's IMPERSONATED_BY_KEY spelling, not the card's impersonatedBy) into its own sys_audit_log row; revoke's row unchanged.",
      "tests": "New sys-scim-connection-credential-actions.test.ts (6 cases). two-factor-one-shot-reveal.test.ts: mint route added to ONE_SHOT_ROUTES (token). platform-admin-affordance-standing.test.ts: both sites in the family floor. plugin-auth scim-connection-admin.test.ts: impersonation mint case (forged body key ignored). Full suites at 82b61834: platform-objects 118 files / 1644 passed; plugin-auth 232 files / 4190 passed, 10 skipped. Ablations via scripts/ablation-replace.mjs, each landed, red, restored blob == HEAD: mint ai.exposed true (exposure pin red), revoke visible without the active guard (visibility pin red), resultDialog secret path token changed (one-shot class pin + mint pin red), revoke without requiresConfirmation (C pin red), plugin-auth always writing impersonated_by (ordinary-mint exact-metadata pin red). Sources are imported from src in both suites, so no dist leg.",
      "gates": "typecheck (platform-objects + plugin-auth, incl. check:test-typecheck) :: exit 0 (first run exit 1 on a TS18048 in the new test, fixed in 82b61834). eslint --no-inline-config packages/platform-objects packages/plugins/plugin-auth :: exit 0. check-i18n-bundles --write re-run: no diff; pnpm check:i18n :: exit 0. dispatch-gates --commands derived 63 families at e2bf9dbe, identical at 82b61834; all 63 run with exit codes; two exited 3 first (check-plugin-teardown-shape --self-test: fixture commit fetched; check:dual-build-cjs-loads: packages built by check:type-check-debt) and re-ran exit 0. --ran: 63 derived, 63 run, 0 NOT-MEASURED (derived zero). CI check-runs: in_progress at report time (PR just opened).",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 — fleet-write relay pr_create (PR #31119); this os-dev-report comment via post-stamped.mjs on #31032; git push is not a REST write",
      "open_questions": [
        {
          "question": "Setup navigation: no entry reaches sys_scim_connection_credential (SETUP_NAV_CONTRIBUTIONS and plugin-auth's contributions name it nowhere), so the actions exist but a platform admin has no menu item to the list.",
          "options": [
            "A: a follow-up card adds a SCIM Credentials item to plugin-auth's navigationContributions beside the SSO one, contributed only while SCIM is wired (auth-plugin.ts; open PR #31013 touches that file)",
            "B: a static entry in platform-objects setup-nav.contributions.ts (no scim public auth feature exists to gate it, so it would show while SCIM is off and the mint would answer 503)",
            "C: leave it; operators open the object by URL"
          ],
          "recommendation": "A, because it follows the SSO precedent (ADR-0029 K2 owning-plugin-contributes) and shows the entry only when the mint can succeed"
        },
        {
          "question": "ADR-0011's identity-actions addendum table (read by reviewers, no automatic check) does not list mint_scim_credential (H) or revoke_scim_credential (C). Updating it is a Tier H ADR edit outside this card's file surface.",
          "options": [
            "A: the seat carries a one-line-per-action ADR amendment as its own Tier H PR",
            "B: fold it into this PR (makes #31119 Tier H)",
            "C: leave the table stale"
          ],
          "recommendation": "A, keeps this PR off the governed surface"
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: none · the revoke door's sys_audit_log row does not carry impersonated_by on an impersonation session (item 4 was scoped to the mint door by the order; same few lines in writeCredentialAudit's revoke caller) · noted, not filed",
        "carrier: none · per-action AI-exposure pins added here, as the order asked; the ADR-0011 addendum records the maintainer striking a table-wide automatic check, and these pins are scoped to the two new actions only · noted for the seat's review"
      ],
      "deviations": [
        "Item 4 key spelled impersonated_by (the audit writers' IMPERSONATED_BY_KEY), not the card's metadata.impersonatedBy",
        "es-ES result-dialog token label rendered Token de portador rather than the loanword Token, so no new en-echo row enters the es-ES provenance table",
        "pnpm lint has no per-package script; ran root eslint (same flags) over the two touched package directories"
      ],
      "line_budget": "12 files, +504 / -2 (under the 3000 human-merge threshold); 216 of the added lines are generated translation bundles",
      "files_changed": [
        ".changeset/scim-credential-mint-revoke-actions.md",
        "packages/platform-objects/src/identity/sys-scim-connection-credential.object.ts",
        "packages/platform-objects/src/identity/sys-scim-connection-credential-actions.test.ts",
        "packages/platform-objects/src/identity/two-factor-one-shot-reveal.test.ts",
        "packages/platform-objects/src/platform-admin-affordance-standing.test.ts",
        "packages/platform-objects/src/apps/translations/en.objects.generated.ts",
        "packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts",
        "packages/platform-objects/src/apps/translations/zh-Hant.objects.generated.ts",
        "packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts",
        "packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts",
        "packages/plugins/plugin-auth/src/scim-connection-admin.ts",
        "packages/plugins/plugin-auth/src/scim-connection-admin.test.ts"
      ]
    }
  6. added
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    on Nov 7, 2026
  7. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · marchtian · session_017ihL2dFb2QdZEHEJ83cR2B

    Landed · 2026-11-07T14:05Z

    reading value
    PR #31119 MERGED 2026-11-07T14:04:44Z through the merge queue, head 82b61834ef
    this card closed by that PR's Fixes #31032

    State: pm:dispatched removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions