Skip to content

feat: configurable proxy mode — intercept scope × unmatched policy #260

@manojbajaj95

Description

@manojbajaj95

Summary

Authsome's proxy currently uses a single hardcoded behavior: build routes from connected providers only, and pass through any unmatched request. This should become a named, persistent configuration instead of an implicit default.

ADR 0003 explicitly deferred the full policy model. This issue specifies the first step: four named modes covering two independent axes — what traffic the proxy attempts to intercept, and what it does with traffic that doesn't match any route.

The two axes

Intercept scope — what goes into the route table:

  • connected: only providers with an active connection in the vault (current behavior)
  • configured: all registered providers (bundled + custom), regardless of connection status

Unmatched policy — what happens to a request that doesn't match any route entry:

  • allow: forward unchanged (current behavior)
  • deny: block the request — return HTTP 403 for plain HTTP; kill the TCP connection for CONNECT tunnels (HTTPS)

The four modes

Mode Intercept scope Unmatched policy Typical use
connected_allow Connected providers Pass through Default — ergonomic local dev
connected_deny Connected providers Block Lock down agent egress to only actively-credentialed providers
configured_allow All configured providers Pass through Surface requests to known-but-unauthenticated providers in the audit log
configured_deny All configured providers Block Strict egress: agent can only reach providers that are registered and credentialed

Configuration

The mode is stored in GlobalConfig as a new proxy sub-block, parallel to the existing encryption block:

class ProxyConfig(BaseModel):
    mode: Literal[
        "connected_allow",
        "connected_deny",
        "configured_allow",
        "configured_deny",
    ] = "connected_allow"

class GlobalConfig(BaseModel):
    ...
    proxy: ProxyConfig | None = Field(default_factory=ProxyConfig)

It is not a CLI flag — it is persisted state, set via something like authsome config set proxy.mode configured_deny.

Behavior details

Intercept scope:

  • connected — route table built from list_connections(); only providers with stored, active credentials are matched (same as today)
  • configured — route table built from list_providers() (bundled + custom); a request that matches a configured provider host but where credential resolution fails is logged as proxy_no_credentials and then subject to the unmatched policy

Unmatched policy:

  • allow — existing pass-through behavior
  • deny — applies to both plain HTTP and HTTPS (CONNECT tunnels); a denied request is audit-logged as proxy_deny with host and reason

Invariants that hold across all four modes:

  • Loopback hosts (127.0.0.1, localhost, ::1) always pass through — blocking them would break proxy-internal communication
  • Provider OAuth/auth endpoints always pass through — blocking them would break the login flow itself

Implementation sketch

  • ProxyRouter.resolve() already returns RouteResolution with a miss_reason. The deny policy acts on miss_reason == "no_match" cases. (ambiguous already errors today.)
  • configured scope requires proxy_routes() in AuthService to optionally draw from list_providers() instead of list_connections() as the host-matching source.
  • Proxy mode should be read at proxy startup / router build time, not per-request.
  • AuthProxyAddon needs the current proxy mode to decide whether to call flow.kill() on misses. The ProxyClient protocol (in both runner.py and server.py) should expose a proxy_mode() method so the addon doesn't couple directly to GlobalConfig.
  • Default connections only — non-default connections are out of scope.

Out of scope for this issue

  • Per-provider allowlist/denylist overrides
  • "warn" mode (log + pass through)
  • Non-default connections
  • Hosted/multi-user mode

Related

  • ADR 0003: Unmatched Proxy Requests Pass Through In Local Mode — this issue supersedes the deferred decision recorded in its Consequences section. If implemented, ADR 0003 should be updated to reference this feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions