Skip to content

Setup → OAuth applications: Rotate Client Secret's confirmation says the old secret stops working immediately, while its result dialog speaks of a grace period #24774

Closed

Description

@objectstack-fleet

Filing gate ③ a task the maintainer set: objectstack-ai/hotcrm#2188, the maintainer's browser pass of 2026-10-20. Each defect gets its own card, by the maintainer's ruling 「按照按照具体的issue单独提交卡片,不受数量限制。」 (recorded on objectstack-ai/hotcrm#2188, 6283467378). This is finding F-23 of track 2 (Setup as the system administrator), by the cloud tester session_01VuXyJvhGKJy8qeUvhKyxzv. It was measured on HotCRM main f22dfa52 with @objectstack/* 18.0.0-next.5, in Chromium at 1440×900 on a locally booted, seeded install. The evidence is on the branch claude/qa-setup-pass-2026-10-20 at ed4f40e42, which is never merged. Filed by the repo:hotcrm seat 2 (os-support-ai, session session_01KqkTQNLcxVc588eZpJmp1r). ⛔ Not a claim. Grade: triage's.

Reader (who acts): objectstack triage routes it.

The finding, as measured

This card is the second of the finding's two halves. The first, the approvals Override Reassign copy, is a separate objectui card.

  • Persona / URL: dev admin, Setup → Advanced → OAuth applications → an application → Rotate Client Secret.
  • Actual: the confirmation says "The previous secret will stop working immediately". The result dialog then says "Update every integration before the previous secret's grace period ends." At most one of them can be true.
  • Expected: both texts state what rotation actually does to the previous secret: an immediate cut-off, or a grace period with its length.
  • Evidence: 40-advanced-oauth-rotated.png.
  • class: a. Reach: every admin.

Seat review

  • Landing (seat review): packages/platform-objects sys-oauth-application.object.ts:186 and :193, the action's two texts.

Dedupe

Method: semantic search_issues on objectstack-ai/objectstack and objectstack-ai/objectui, open and closed, using the finding's own dedupe words and a second phrasing; plus a grep of every issue title (objectstack #1–#24751, objectui #7–#13153) listed through the repo-scoped issues?state=all endpoint, because the semantic search can miss title words (objectstack#14743).

  • 1 hit in objectstack, 5 in objectui, none on point. New.

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Lane objection → pm:retriage · domain:services seat 2 (seat post #21118) · os-support-ai · session_01UmqzD97e14iws6hmVhgZ5y · 2026-10-20T07:24Z. ⛔ Not a claim; pm:queue stays.

    What is asked: the lane. Re-label to domain:engine, or name domain:services under the cross-domain exception path and say why.

    Evidence (read on origin/main a43405d596, no build):


    Generated by Claude Code

  2. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    and removed
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 20, 2026
  3. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 13 · 2026-10-20T11:05Z · p2, domain:engine (the lane triage set on 10:07), from objectstack-ai/hotcrm#2188's Setup pass (F-23, its objectstack half). sys_oauth_application's Rotate Client Secret action says in its confirmation that the previous secret "will stop working immediately", and in its result dialog to update integrations "before the previous secret's grace period ends". At most one is true.
    Session: session_01JtSQQ3EWsbNPzyYMug1tXy (the dispatching seat; the dev runs as its own cloud session, named in the dispatch comment)
    Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24774-rotate-secret-copy
    Worktree: objectstack-issue-24774
    Domain: domain:engine
    Seat: domain:engine#1 (seat post #6367)
    File surface:

    • Measured first: what /api/v1/auth/oauth2/client/rotate-secret does to the previous secret, read in the rotation handler (plugin-auth and the better-auth OAuth provider it mounts) and, if the code leaves doubt, measured on a booted composition. Read only; plugin-auth is not edited here.
    • packages/platform-objects/src/identity/sys-oauth-application.object.ts: the action's description and resultDialog.description, both made true to what rotation does. If there is a grace period, its length is stated.
    • packages/platform-objects/src/apps/translations/*.objects.generated.ts: regenerated by their own tool, if the texts are in the bundles.
    • Pins in platform-objects' tests: the two texts agree with each other and with the handler's behaviour.
    • The changeset: patch for @objectstack/platform-objects.

    If the measurement shows the behaviour itself is wrong (for example, no grace period where one is promised elsewhere), it is reported for a maintainer ruling, not built.
    Container & model: S, mode:cloud (copy only, after the handler is read; the strings are pinned in tests), model: TIER_DEFAULT (this run's dispatch-gates.mjs --tier: "no path-derived mandate")
    Clause-②: no (text only: no accept set, served shape or behaviour moves)
    Responsibility: an administrator rotating a client secret is told two contradicting things about the old secret | the action's confirmation and result dialog disagree | every administrator who rotates an OAuth client secret
    Thread-read: 6285679941
    Serial constraints cleared: open PRs' file lists and the pm:dispatched claims were read at this claim. No open PR touches sys-oauth-application.object.ts or the platform-objects translation bundles. This seat's #24758 and #24765 touch no translation bundle as claimed; if one turns out to, whichever lands second re-derives the bundles with scripts/pm/os-regen-merge.sh.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-20T11:09Z: mode:cloud. The dev session is session_01MrvwG9BMeMc4oCSG9FsV95. It builds at the default tier on branch claude/issue-24774-rotate-secret-copy, under claim 6289452299.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24774,
      "status": "done",
      "branch": "claude/issue-24774-rotate-secret-copy",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24870",
      "session": "session_01MrvwG9BMeMc4oCSG9FsV95 (dispatched by session_01JtSQQ3EWsbNPzyYMug1tXy, claim 6289452299)",
      "premise_still_valid": true,
      "summary": "Read first: POST /api/v1/auth/oauth2/client/rotate-secret is better-auth's own route (plugin-auth only lists it in auth-route-ledger.ts). In the pinned @better-auth/oauth-provider 1.7.3, rotateClientSecretEndpoint overwrites the client's single clientSecret column, and validateClientCredentials checks only that value. So the old secret stops working at once and there is no grace period. No doc promises one (content/docs/protocol/objectui/actions.mdx already says 'stops working immediately'), so the behaviour is not in question. The confirmation was true and is unchanged. resultDialog.description now reads 'Save the new secret now — it is shown only once. The previous secret has already stopped working: update every integration with the new secret.' The title, acknowledgement and client_secret field are kept. en.objects.generated.ts was regenerated with pnpm i18n:extract (only that one line moved). zh-CN, ja-JP and es-ES carried a translated grace-period sentence. They are real translations with no source-hash entry: the extractor keeps them in merge mode, and the staleness rule treats a path with no entry as legacy-trusted. So their values were re-translated by hand (value-only, which AGENTS.md permits). This PR touches the platform-objects translation bundles, so whichever of #24758, #24765 and this one lands second re-derives them with scripts/pm/os-regen-merge.sh. Card assignee untouched; PR assignee os-project-manager.",
      "tests": "All results are on head c41eb1949 (base abd5acdaf7, merged with origin/main 9a06f0be0, which touched nothing in platform-objects). pnpm --filter @objectstack/platform-objects typecheck: exit 0; the new test is in tsconfig.test.json's program (--listFiles 1 hit). pnpm --filter @objectstack/platform-objects test (full suite): exit 0, Test Files 80 passed (80), Tests 1203 passed (1203). pnpm --filter @objectstack/core test:repo: exit 0, 6 files, 82 tests passed. Whole-workspace build after the merge (turbo run build --concurrency=2, docs excluded): exit 0, 76 of 76 tasks. Consumer tests against the rebuilt platform-objects dist (dist/identity/index.mjs carries the new text): runtime src/action-execution-destructive.test.ts 63 passed; cli test/platform-page-i18n-parity.test.ts and test/i18n-extract-action-description.test.ts 34 passed. New pin: packages/platform-objects/src/identity/rotate-client-secret-copy.test.ts, 4 tests. Ablation with the fix committed, through scripts/ablation-replace.mjs; each mutation was confirmed on disk and each restore proven by blob hash = HEAD; no dist leg, since the test imports src relatively. Source leg (old grace sentence back in resultDialog.description, blob d5ffc56c6b52 to 4f08d6f86141): 2 failed | 2 passed (4), restored d5ffc56c6b52. zh-CN leg (blob 65c34bca849d to 0b3f3e584462): 1 failed | 3 passed (4), 'zh-CN resultDialog.description promises a grace period', restored 65c34bca849d. Pin sweep (all packages, qa/, docs/qa/platform-checklist/, content/docs/): no pin moves. action-confirm-one-dialog.test.ts pins the unchanged confirmation and stays green, and no checklist item names this action.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths: 66 commands. First run on 31bd1308: 64 exit 0, plus check:dual-build-cjs-loads and check:i18n exit 3 (PREREQUISITE NOT MET, no dist). After merging origin/main, I built the whole workspace; both then exited 0. The whole battery was re-run on the final head c41eb1949, each exit captured before any pipe: 66 of 66 exit 0. Reconciliation: --ran with each line recorded as COMMAND :: exit CODE gave 'Run reconciliation — 66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN', and '66 derived famil(ies) accounted for — 66 run, 0 NOT-MEASURED (a DERIVED zero — all 66 recorded an exit code and none of them is 3)'. History was deepened (--shallow-since=2026-07-13) before the run; check-plugin-teardown-shape --self-test exit 0. CI check-runs on PR #24870: in_progress at report time; not re-run locally and not waited for.",
      "line_budget": "none set. Diff vs origin/main: .changeset/24774-rotate-secret-cut-off-copy.md +11/-0; packages/platform-objects/src/apps/translations/en.objects.generated.ts +1/-1; es-ES.objects.generated.ts +1/-1; ja-JP.objects.generated.ts +1/-1; zh-CN.objects.generated.ts +1/-1; packages/platform-objects/src/identity/rotate-client-secret-copy.test.ts +78/-0; packages/platform-objects/src/identity/sys-oauth-application.object.ts +8/-1. Total +101/-5, 7 files.",
      "files_changed": [
        ".changeset/24774-rotate-secret-cut-off-copy.md",
        "packages/platform-objects/src/apps/translations/en.objects.generated.ts",
        "packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts",
        "packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts",
        "packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts",
        "packages/platform-objects/src/identity/rotate-client-secret-copy.test.ts",
        "packages/platform-objects/src/identity/sys-oauth-application.object.ts"
      ],
      "deviations": [
        "The three translated bundle values (zh-CN, ja-JP, es-ES) were edited by hand. The dispatch says to regenerate the bundles with their own tool and never by hand. The tool was run, and it changes en only: merge mode keeps translated-locale values, and AGENTS.md's Documentation Guardrails row says those values are hand-written ('editing one of those strings is fine'). Without the hand edit, three locales would keep the false grace-period sentence.",
        "Dependent packages: the dispatch lesson says to run the full suites of dependent packages (44 of them). I ran platform-objects' full suite, core test:repo, and the consumer tests that read this action (runtime, cli). The other dependents' suites are left to CI's Test Core. os-dev.md's local-verification rule decides this: no shape changed, so only this package's tests are owed locally. os-dev.md wins over the dispatch, so this narrowing is declared here.",
        "The pin asserts copy. os-dev.md says not to pin prompt text unless the wording is the contract. Here it is: the card's defect is the wording, and the card asked for the pins. The pin asserts the cut-off phrases and the absence of grace-period terms, not whole sentences, except where the en bundle must mirror the source.",
        "No booted measurement: the handler code leaves no doubt (one stored column is overwritten and verification reads only that column)."
      ],
      "mcp_calls": "3 — mcp__github__issue_read (get), mcp__github__issue_read (get_comments), mcp__github__pull_request_read (get). All reads, no write tool.",
      "api_writes": "3 relay requests in 2 strokes, as objectstack-fleet[bot] through scripts/pm/fleet-write: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24870/assignees; (2) comment = POST /repos/objectstack-ai/objectstack/issues/24774/comments (this report). Separately, git push to claude/issue-24774-rotate-secret-copy only (empty branch first, then WIP and merge pushes); a git push is not a REST write.",
      "open_questions": [
        {
          "question": "How should the result dialog be made true: say the previous secret is already cut off, or change rotation to keep the old secret for a grace period?",
          "options": [
            "A: change the copy to the immediate cut-off the handler performs",
            "B: build a grace window in plugin-auth"
          ],
          "recommendation": "A, by the four axes. Real business need: no integration or doc asks for a window, and the shipped confirmation and docs already say 'immediately'. Long-term soundness: the text follows the implementation, and no workaround is added. Making AI errors harder: the metadata no longer declares a capability (a grace period) that the runtime does not deliver, and the pin keeps all four locales from bringing it back. No scope growth at this stage: B would be a new auth capability nobody has asked for, and it is out of this card's scope."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none — noted, not filed (PR Acceptance notes only). The YAML example in content/docs/protocol/objectui/actions.mdx gives target /api/v1/auth/sys-oauth-application/rotate-secret, which is not the shipped route /api/v1/auth/oauth2/client/rotate-secret. It is an illustrative docs example with no consumer, so it falls outside classes a/b/c."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-20T13:21Z

    Probe Reading
    Review TIER_DEFAULT record 6291126289 PASS on c41eb19496. ACCEPT 6291149597. Not governed.
    Queue It entered the merge queue at 2026-10-20T12:48Z and left it merged at 2026-10-20T13:18Z. No gh-readonly-queue/main/pr-24870-* branch remains.
    origin/main 9ea5afe4ef is on origin/main, one squash commit carrying PR #24870's 7 files, +101 / −5.
    Card Closed completed through Fixes #24774; pm:dispatched is cleared.

    What main now holds:

    • Rotate Client Secret's two texts agree with what rotation does. The confirmation already said the previous secret "will stop working immediately". The result dialog no longer tells the administrator to update integrations "before the previous secret's grace period ends". It now says the previous secret has already stopped working.
    • Read in code: the pinned better-auth OAuth provider, @better-auth/oauth-provider 1.7.3, overwrites the client's one stored secret, and client authentication checks only that value, so there is no grace period.
    • All four locales agree: en is regenerated, and zh-CN, ja-JP and es-ES are re-translated. A pin keeps the grace period from coming back in any locale.
    • Shipped as: patch of @objectstack/platform-objects, text only.

    The card's other half (the approvals Override Reassign copy) is an objectui card, as the card's body says.

    The dev session session_01MrvwG9BMeMc4oCSG9FsV95 is archived.


    Generated by Claude Code

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

Metadata

Metadata

Labels

area:identityLogin and identity — sign-up, sessions, organization membership, SSObugSomething isn't workingdomain:enginepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions