Skip to content

feat: mint the viewer's authority per consumed method, not per module - #40

Merged
AntoineToussaint merged 1 commit into
feat/consumed-module-passthroughfrom
feat/per-method-passthrough-scopes
Sep 26, 2026
Merged

AntoineToussaint merged 1 commit into
feat/consumed-module-passthroughfrom
feat/per-method-passthrough-scopes

Conversation

@antoinetoussaint-byte

Copy link
Copy Markdown
Contributor

What

A consumed method may declare its own Scopes (ConsumedMethod.Scopes []Scope). They replace the module's Scopes for a call to that method — never add to them. So one module whose methods need different authority is declared under one as, for example a member's list and an administrator's inspect over the same resource kind, and each call mints only its own method's authority.

  • A viewer who lacks one method's authority is refused that method as permission_denied. The message carries the issuer's reason, which names the missing permission (accounts answers owner is not allowed <kind>:<action> at requested scope). Every other method is still served. The mint is never widened, and never retried with less.
  • Mint refusals are a typed WorkContextRefusal{Audience, StatusCode, Code, Message}. It still unwraps to *GatewayError, so existing errors.As callers are unchanged. The passthrough relays a 4xx refusal in the issuer's own Connect code. Before this, a refused mint reached the page as a bare Forbidden.
  • Fail early. Boot refuses:
    • method scopes on a ViewerBearer module;
    • a non-ViewerBearer module where some method has neither its own nor the module's scopes;
    • a scope with no resource kind, no action, or a blank or repeated action.
  • PassthroughOperations documents each method's own effective scopes.

Classification: fix. The all-or-nothing mint per module made a solution choose between breaking a method for every viewer who lacks one permission and never offering it at all. The mint is decided where it is owned: the passthrough's declaration.

Base

This is stacked on #39 (feat/consumed-module-passthrough, head 8ea2cca). The passthrough (passthrough.go, ConsumedModule) exists only there, not on main. Retarget onto main once #39 lands. See the repo's note on stacked PRs: CI runs only against main, so this PR gets no CI until it is retargeted.

Host

No host change is needed. Accounts' StartTaskWorkContext already resolves each requested kind:action against the owner's current role assignments and refuses one it does not hold with PermissionDenied, naming it. A narrower mint is just a smaller scope list.

Verified

  • go build ./... && go test -race ./... && go vet ./... pass (Go 1.x, this checkout).
  • New tests:
    • each call mints only its own method's scopes, never the union;
    • method scopes replace the module's;
    • a refused inspect mint gives 403 permission_denied, the message names things:inspect, and the module is never called, while the list method is still served with a list-only mint;
    • every boot refusal case;
    • the documented callers per method.

Not verified

  • This has not been run against a live accounts + auth-gateway. The refusal body shape comes from reading the accounts handler, not from a live mint.
  • It has not been exercised from a consuming solution's page yet.

🤖 Generated with Claude Code

A consumed method may declare its own Scopes, which replace the module's for a
call to that method. One module's methods that need different authority are
declared under one `as`, and each call mints only its own method's scopes, so
a viewer who lacks one method's authority is refused that method as
permission_denied (with the issuer's reason, naming the permission) and is
still served the others. Declarations that could never mint are refused at
boot. A mint refusal is now a typed WorkContextRefusal that still unwraps to
the GatewayError it was.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AntoineToussaint
AntoineToussaint merged commit ca2fac1 into feat/consumed-module-passthrough Sep 26, 2026
1 check passed
@antoinetoussaint-byte
antoinetoussaint-byte deleted the feat/per-method-passthrough-scopes branch October 25, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants