Repository navigation
fix(auth): AuthGuard requiredRoles reads positions; retire the roles mirror key - #5724
Merged
Merged
Conversation
…mirror key (#5424) The residual of objectui#5424 after PR #5555 landed sites 2/3/4, per the maintainer's 2026-08-22 ruling (Option A): - AuthGuard's requiredRoles matches against user.positions (framework ADR-0090 D3's one published spelling), keeping the user.role scalar fallback only when the session carries no positions key at all. This fixes both live failure directions: position-holders refused at their own gates (protocol 17 never emits roles), and the coarse scalar admitting where the server says the position is absent. - Preview mode emits positions: [role] instead of the retired roles key — it was the LAST producer — so preview identities match the protocol-17 shape for every positions consumer and keep passing gates exactly as before. - The hand-copied roles?: string[] mirror key retires from the client AuthUser now that its last reader is gone; any future read of user.roles dies at compile time via the index signature. Pins in authGuardPositions-5424.test.tsx cover both directions: restored admission, closed over-admission, refusal with neither credential, present-but-empty positions staying authoritative, the preserved scalar fallback, the retired spelling staying dead, and preview continuity plus the producer retirement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuPCi56cnGyykygi3z9w4m
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-zhuang
marked this pull request as ready for review
August 23, 2026 01:06
This was referenced Aug 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5424
The residual of the card after PR #5555 landed sites 2/3/4, implementing the maintainer's 2026-08-22 ruling (同意 / Option A): the gates read
positions.What changed
packages/auth/src/AuthGuard.tsx—requiredRolesnow matches againstuser.positions(framework ADR-0090 D3's one published spelling), keeping theuser.rolescalar fallback only when the session carries nopositionskey at all (guest / legacy identities). A presentpositionsarray is authoritative, empty included — falling back to the scalar on a present-but-empty array would re-open the over-admission this card closes. The retiredrolesspelling is not read.packages/auth/src/AuthProvider.tsx— preview mode emitspositions: [role]instead ofroles: [role]. It was the last producer of the retired key; a preview identity is now shaped like a protocol-17 session for everypositionsconsumer (this gate, the approver-identity derivations, workspace-admin leg 3). Measured before changing it: with the new gate and the old producer, preview users still passed every gate via the scalar fallback — so this half is producer retirement and shape consistency, not what keeps demos alive. The guest identity (enabled={false}) is untouched: it never producedrolesand the scalar fallback covers it, behavior-identical.packages/auth/src/types.ts— the hand-copiedroles?: string[]mirror key retires now that its last reader is gone. Verified the promised compile-time death: a temporaryuser?.roles?.includes('admin')probe fails@object-ui/authtype-check withsrc/AuthGuard.tsx(55,21): error TS2339: Property 'includes' does not exist on type '{}'— the same signature the card measured when the removal was attempted while site 1 still read the key.Explicitly NOT done, per the ruling: no compatibility shadow key re-emitting
roles;packages/permissions' separaterolessurface untouched.Both directions pinned (
authGuardPositions-5424.test.tsx)This is an access-control gate, so the pins cover refusals as much as admissions:
positionsis presentpositions→ refusedpositionskey → scalar still admitsrolesspelling → refusedAuthProvider.test.tsx)positions: [role],hasOwnProperty('roles')falseFixture triage:
AuthProvider.test.tsx's "one of the required roles" fixture spelled the arrayroles— it stayed green precisely because of the alias branch this PR deletes. Converted topositionsand given a companion negative pinning the retired spelling refused.Verification (all at
cc6e27d7a, the head of this PR)pnpm exec vitest run packages/auth/— 20 files, 209 tests passed (verify-lock verdict: command-exit 0).AuthGuard.tsxreverted toorigin/main, mutation confirmed on disk by grep): exactly the 4 direction pins fail (4 failed | 5 passed), the widening guard, scalar fallback and preview cases stay green; restored, tree clean.AuthProvider.tsxreverted alone): only the producer-retirement pin fails (1 failed | 8 passed) — the two preview gate cases staying green is the direct measurement that the scalar fallback alone covers preview at this gate.turbo run type-check --filter=...@object-ui/auth(auth and its dependents — downstream consumer direction): 40/40 tasks successful (verify-lock verdict: command-exit 0).turbo run lint --filter=@object-ui/auth(plus root lint): 0 errors (29 pre-existing warnings, none in this diff; the 4 warnings on touched files pre-date it). Narrowed deliberately:eslint.config.jshas no type-aware linting (projectService/parserOptions.projectabsent) and no custom rule reads other files, so this diff cannot move an untouched package's verdict; CI runs the full farm regardless.check:control-bytes✅ (4778 files) ·check:phantom-deps✅ ·check:esm-specifiers✅ ·changeset:check✅ (nomajor) ·check-changeset-presence✅ (.changeset/authguard-positions-5424.md,minor).Breaking only for TypeScript consumers that compiled against
AuthUser['roles']— no protocol-17 payload ever carried the key at runtime. Declared in the changeset body;minorper this repo's version-alignment policy.Generated by Claude Code