Skip to content

Auth-required gate: DEFER park/resume for delegated MCP consent (#317 follow-up) #330

Description

@initializ-mk

Carved out of the #317 epic. The delegated token + connection resolution landed (#327 + #329); this is the remaining Forge-side half — the runtime auth-required gate that turns "no grant yet" from a hard failure into a pause that resumes once the user consents. Forge, every mode (§18.4).

Problem

Today a type: user MCP call with no platform grant for the requesting user returns ErrNoToken — the tool call fails. #317's contract is that it should pause: the executor parks, the user is prompted to consent, and the call resumes with their token once the grant exists. Without this, a delegated tool is unusable until the user has separately consented out-of-band.

today:   BearerToken(type=user, no grant) → ErrNoToken → tool call fails
target:  BearerToken(type=user, no grant) → PARK the executor
             → deliver an auth-required prompt (platform consent / standalone loopback)
             → user consents → platform has a grant → RESUME → call proceeds

Scope (Forge, every mode)

Generalize the DEFER park/resume engine (R4c, deferpolicy.Engine / Handle.WaitCtx) into a reusable "pause the tool call, request X, resume on signal" primitive, and add an auth-required variant:

  1. Auth-required gate — when a delegated resolver returns ErrNoToken (no grant yet), park the executor on a Handle keyed by {task/session, server, subject} instead of failing. Resume when a grant becomes available (a signal / re-poll succeeds) or time out → fail.
  2. Signal to resume — a resume endpoint (mirrors POST /tasks/{id}/decisions) the platform calls when consent completes; or a bounded re-poll of the delegated resolver.
  3. Delivery is mode-split (§18.4):
    • Managed: the platform prompts (Slack DM / console chat), hosts the callback, and signals resume — Forge only parks/resumes. (Delivery itself is platform-side.)
    • Standalone default: Forge's own loopback callback + state binding + AuthLinkDeliverer (Slack ephemeral / A2A auth-required artifact) — the interactive resolver.

Acceptance (the #317 boxes this closes)

  • A type: user tool call lacking a grant pauses (not fails) and resumes after consent.
  • state binding rejects cross-session / replayed / expired callbacks (standalone default).
  • Managed: Forge parks/resumes only; callback + consent + token custody are platform-side.
  • Session token store is a swappable interface a broker-backed impl substitutes (§18.8 UI/cli ux update #2) — formalize beyond the current per-subject cache.
  • Never mints speculatively — park until authorized, then resolve (delegation follows authorization, §18.5).

Already in place (build on these)

Out of scope (elsewhere)

Related

Epic: #317 · tokens #327 · connections #329 · broker #318 · AARM R10 #319 · authority design-tool-registry.md §18.4/§18.5.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestforge-coreAffects the forge-core library (runtime, security, types, llm, mcp, auth)securitySecurity vulnerability fixes

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions