refactor(auth): make AccessControl.boundAuth required, drop dead unauth branch#4181
Open
viktormarinho wants to merge 1 commit into
Open
refactor(auth): make AccessControl.boundAuth required, drop dead unauth branch#4181viktormarinho wants to merge 1 commit into
viktormarinho wants to merge 1 commit into
Conversation
…th branch `boundAuth` is built by the context factory for EVERY request (even anonymous), so it is never absent at runtime. Type it as a required constructor dependency and delete the code that only ran when it was missing: - `check()`'s `!userId && !boundAuth` branch (the UnauthorizedError 401 + public- tool fallback) was dead in prod — an anonymous caller already gets 403 via the permission check, and public tools are gated by the MCP transport's own `isPublicTool` and the `MESH_PUBLIC_` prefix. Removed it, along with the now-unused `isToolPublic`, the `getToolMeta` constructor param, the `GetToolMetaFn` type, and the `MCP_MESH_KEY` import. - Dropped the dead `if (!boundAuth) return false` guards in `checkResource` / `checkApiKeyAccess` / `checkMemberAccess`. - `userId`/`toolName` become required-but-nullable params (may be undefined, but are always passed positionally). No behavior change: the removed branch never executed in production. Tests that constructed AccessControl without a boundAuth now pass a mock (grant/granted), or were deleted (the "no BoundAuthClient" case, which can no longer happen). Note: AccessControl no longer throws UnauthorizedError (nothing in the repo does now). The type and its 401 mappings in tools-rest/org-fs are retained pending a decision on anonymous-caller semantics (401 vs the current 403). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Follow-up to #4180 (the
isApiKeyPrincipalflag landed there raised the question: why isboundAuthoptional at all?).Why
boundAuthis built by the context factory for every request — including anonymous ones (no role/perms, then denied by the permission check). So it's never absent at runtime, yet it was typed optional, which left dead "should not happen" guards and a whole unreachable branch.What
boundAuthis now a required constructor param.userId/toolNamebecome required-but-nullable (string | undefined) so the positional signature is legal.!userId && !boundAuthbranch incheck()— theUnauthorizedError401 + public-tool fallback. It only ran whenboundAuthwas missing (impossible). Anonymous callers already get 403 via the permission path, and public tools are gated by the MCP transport's ownisPublicTool(auth.ts) and theMESH_PUBLIC_prefix. With it went the now-unusedisToolPublic, thegetToolMetaconstructor param, theGetToolMetaFntype, and theMCP_MESH_KEYimport.if (!boundAuth) return falseguards incheckResource/checkApiKeyAccess/checkMemberAccess.new AccessControl()constructions now pass a mockboundAuth(still testing grant/granted); the "should deny access when no BoundAuthClient provided" test is deleted (it asserted an impossible state); the "no userId" test now assertsForbiddenError(the 401 path is gone).No behavior change — the removed branch never executed in production. Net −50 LOC.
Heads-up / follow-up decision
Removing that branch removed the last
throw new UnauthorizedErrorin the repo, so the type is now never thrown (itsinstanceofcatches intools-rest.ts/org-fs.tsare dead). Left as-is here. Open question: dropUnauthorizedError+ those catches, or restore 401 semantics by making truly-unauthenticated callers throw it (anon → 401 instead of today's 403)? That's a product call, not part of this cleanup.tsc+oxlintclean; auth+core unit tests green (except the pre-existing DB-gatedcontext-factory.integration.test.ts).🤖 Generated with Claude Code
Summary by cubic
Make
AccessControl.boundAutha required dependency and remove the unreachable unauthenticated branch to simplify auth checks with no behavior change. Anonymous requests still get 403 via permission checks; public tools continue to be handled upstream in the MCP transport.boundAuthinAccessControlconstructor;userIdandtoolNameare now required-but-optional (string | undefined).!userId && !boundAuthpath and its public-tool fallback; deleteGetToolMetaFn,isToolPublic,MCP_MESH_KEY, and stop passinggetToolMetafrom the transport and context factory.if (!boundAuth)guards incheckResource,checkApiKeyAccess, andcheckMemberAccess.boundAuth; remove the “no BoundAuthClient” test; unauthenticated case now expectsForbiddenError. UnauthorizedError is no longer thrown, existing catches remain for now.Written for commit df48818. Summary will update on new commits.