stacklok / stacklok/toolhive

Feature Request: Authorization Context Enrichment via Rego Policies

Open
#4,112 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

api authentication authorization enhancement go
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Summary

ToolHive's authorization middleware currently produces generic, coarse-grained
operation and resource identifiers for MCP requests. For example, a tools/call
to list_commits on the GitHub MCP server produces:

  • operation: mcp:tool:call
  • resource: mrn:mcp:github:tool:list_commits

These identifiers are sufficient for policies like "allow all tool calls on the
github server", but they carry no argument context. This makes it impossible to
write fine-grained policies such as "allow list_commits only on repos the user
owns" because the target repository is never surfaced to the PDP.

Proposed Solution

Add a context enrichment stage to the authorization pipeline. Before a
non-list MCP request is sent to the PDP, an optional user-defined Rego policy
runs and may transform the operation/resource identifiers based on full request
context (tool arguments, server ID, feature, etc.).

With enrichment enabled, the same list_commits call could produce:

  • operation: mcp:tool:github:commit:list
  • resource: mrn:toolhive:github:repo:owner/my-repo

The PDP can then enforce repository-scoped or verb-scoped policies that would
not be possible today.

Design
New pkg/authz/enrichment package
  • Enricher interfaceEnrich(Input) (*Result, error). Implementations
    can use Rego, schema inference, or any other mechanism.
  • Pipeline – runs enrichers in order; returns the first non-nil result.
    Errors are logged and skipped so enrichment failures never block requests.
  • RegoEnricher – evaluates a Rego policy in package
    toolhive.authz.enrichment; the policy defines a rule named result that
    maps input fields to operation and resource strings.
  • Input / Result types – carry feature, operation, resource ID,
    arguments, and server ID into the policy; carry back enriched strings.
Config

Add an optional enrichment block to the existing authz YAML config:

version: "1.0"
type: httpv1

enrichment:
  rego_policy: "/path/to/enrichment.rego"
  # OR inline:
  # rego_policy_inline: |
  #   package toolhive.authz.enrichment
  #   default result := {}
  #   result := { "operation": ..., "resource": ... } if { ... }

pdp:
  http:
    url: "http://localhost:9000"
  claim_mapping: "standard"
  context:
    include_args: true

Configs without the enrichment section continue to work unchanged. Exactly
one of rego_policy (file path) or rego_policy_inline may be set.

Middleware integration

  • authz.Middleware gains an optional *enrichment.Pipeline parameter (nil
    disables enrichment; existing callers are unaffected in behavior).
  • For non-list operations the middleware runs applyEnrichment before calling
    the authorizer. The enrichment result is stored in the request context.
  • The HTTP PDP authorizer reads the enriched operation/resource from context and
    substitutes them into the PORC before dispatching to the PDP. The
    context.mcp fields retain the original unenriched values so the PDP has
    access to both.
  • List operations are not enriched (they use the existing filter-on-response
    path).

Example policies

Provide a full example enrichment policy for the GitHub MCP server
(examples/enrichment-github.rego) covering all 41 GitHub tools across six
resource scopes (repo, owner, org, search, user, global) with a consistent
noun:verb operation convention, plus an accompanying config example
(examples/authz-httpv1-enrichment-config.yaml).

Scope / Limitations

  • Phase 1: HTTP PDP authorizer (httpv1) only. Cedar (cedarv1) support is
    deferred to a follow-up.
  • Enrichment policies must use OPA v1 syntax (the if keyword is required
    before rule bodies).
  • Only non-list operations are enriched.

Acceptance Criteria

  • pkg/authz/enrichment package with Enricher, Pipeline, RegoEnricher,
    Input, Result, and Config types, with unit tests.
  • authz.Middleware accepts an optional *enrichment.Pipeline; nil is a
    no-op (no behaviour change for existing users).
  • HTTP PDP authorizer substitutes enriched operation/resource when an
    enrichment result is present in context.
  • enrichment block in authz YAML config (rego_policy / rego_policy_inline).
  • docs/authz.md updated with a "Context enrichment" section including
    the input/output contract, configuration reference, and examples.
  • Example Rego policy and config file added under examples/.
  • All existing authz tests continue to pass.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the existing authz middleware and HTTP PDP authorizer, then inspect the requested pkg/authz/enrichment package boundaries and acceptance criteria. Implement and test the enrichment pipeline, configuration, context handling, and HTTP integration described in the issue; update docs/authz.md and add the two examples under examples/. Run the existing authz tests and the new enrichment unit tests to verify unchanged behavior without enrichment.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
authorization, backend-api-design, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.