Skip to content

Identity: the JIT user path reads the IdP locale claim into the user language preference (hosted environments follow the cloud account language) #25261

Closed

Description

@objectstack-fleet

Filing gate ②: the framework half the maintainer ruled on objectstack-ai/cloud#3125 (batch #387 item 1, column 现在立 card 5: 「用户语言偏好随云账号进入每个环境」, platform first). Filed by repo:cloud#1 (seat post #6026, session session_01FSg7JRUiTPo8GrgT38tAWP). ⛔ Not a claim.

Reader who acts: the objectstack lane. The cloud half (the control plane's OIDC provider emits locale) is objectstack-ai/cloud#3159, which is Blocked-by: this one.

Wanted

  • When the environment creates or refreshes a user just-in-time from an external IdP sign-in (the cloud as IdP on hosted environments, or any OIDC IdP), it reads the standard OIDC locale claim (BCP 47) into the user's language preference.
  • On first creation, the claim sets the preference.
  • On a later sign-in, the claim updates it only while the user has not set a preference in the environment itself; a local choice wins.
  • An unknown or unsupported locale falls back to the deployment default, closed against the supported locale list.

Acceptance

  • A JIT sign-in with locale: "zh-CN" creates a user whose console is zh-CN.
  • A later sign-in with locale: "en" after the user picked ja locally keeps ja.
  • An unsupported locale falls back to the default.

Activity

  1. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: card thread · domain:services seat 1 (seat post #6021, project run by the Sales account) · p2 · the whole card · 2026-10-22T21:08Z
    Session: session_019PQiYTjCGrkiAcRc8e5mrU
    Account: os-sales (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-6usz2l (the harness-pinned branch for this session; it names no issue, so the Duplicate Fix Guard's branch-name hint will not match — this claim is the identity record)
    Worktree: objectstack-issue-25261
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    File surface (read on origin/main 49185cd; stop on a breach and explain it in the report):


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 25261,
    "status": "done",
    "branch": "claude/project-thread-6usz2l",
    "pr": "#25303",
    "session": "session_019PQiYTjCGrkiAcRc8e5mrU (card thread)",
    "premise_still_valid": true,
    "summary": "New module plugin-auth/src/idp-locale-claim.ts: the validateUserInfo seam stages the OIDC locale claim off the IdP profile better-auth hands it (source.oauth.profile for generic-OAuth and social providers incl. the cloud IdP, source.sso.profile for sso-oidc), keyed by the request endpoint context; the composed session.create.after settles it into sys_user.locale. First creation sets it; a later sign-in follows the claim only while the column is unset or still holds what the IdP last wrote (recorded per user in a sys_verification row through the better-auth database adapter, the unlink-record idiom); a local choice wins; an unknown or unsupported claim leaves the column unset (clears an IdP-written value) so the deployment default applies, closed against the i18n service getLocales() via core resolveLocale with the supported spelling stored. auth-manager.ts gains only three call sites plus the instance. Changeset patch, Clause-②: no, Metadata-protocol: none.",
    "tests": "idp-locale-claim.test.ts 22/22 (13 pure, 9 end-to-end OAuth round trips through the platform IdP over the in-memory engine, incl. the card's three acceptance cases). Ablation via scripts/ablation-replace.mjs, source-imported subject (no dist leg): settle call removed 5 failed / 17 passed; local-choice guard removed 4 failed / 18 passed; supported-list closing removed 5 failed / 17 passed; each restore proven (blob == HEAD, git diff HEAD empty). On final head 611c99a: pnpm build VERDICT command-exit 0 (76/76 tasks); pnpm --filter @objectstack/plugin-auth test Test Files 186 passed (186), Tests 3515 passed | 10 skipped (3525); plugin-auth typecheck (src, examples, test layer) exit 0. All under os-verify-lock.",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack on 611c99a: 109 derived, all exit 0 (check:type-check-debt under the lock; plugin-teardown self-test after fetching its pinned fixture commit; dual-build-cjs-loads after the full build). --ran: 109 derived, 109 run, 0 NOT-MEASURED, 0 UNRUN. Roster gates under touched dirs (check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity): exit 0. First pass fixes: check:durability-log-level flagged a helper whose name collided with the vocabulary callee writeRecord (renamed replaceIdpLocaleRecord); check:engine-double-contract ledger learned the new test file (--write); check-tenant-audit-census counted the new sys_user write (236 to 237): artefacts regenerated with --write and the six hand-written page figures moved by hand. CI on the PR: in_progress at report time.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "5 — reads only: issue_read x2, search_pull_requests x1, pull_request_read x2 (plus the harness PR-activity subscription, not a GitHub write)",
    "api_writes": "5 relay strokes, all via scripts/pm (objectstack-fleet[bot]): label-write #25261 (assignee os-sales, +pm:dispatched -pm:queue); post-stamped claim comment 6351359330; dispatch pr_create #25303 (draft, assignee os-sales); dispatch pr_request_reviewers #25303 (os-sales); post-stamped this report. git push is not a REST write.",
    "open_questions": [],
    "out_of_scope_findings": [],
    "notes": "File surface: three derived-ledger paths outside the claim (scripts/engine-double-contract.pinned.json, content/docs/permissions/tenant-audit-census.mdx, docs/audits/2026-08-tenant-audit-write-call-sites.counts.md) are the gates' own ledgers for the declared write, declared in the PR body. Serial: the #25224 move-only split of auth-manager.ts is in flight; this change keeps its logic in a new file. PR body written once at pr_create."
    }

  3. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 1 (seat post #6021) · os-sales · session_016g7nYP9wdNx1GDa5XDr7Uy · 2026-10-22T23:13Z

    reading value
    PR #25303 MERGED through the queue (ACCEPT 6352928671)
    origin/main carries 829cb77917

    State: the card closed completed through the PR; this stroke clears pm:dispatched and the assignee. The cloud half (the control plane emitting locale) is objectstack-ai/cloud#3159, outside this repo.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions