Skip to content

Commit 51b0fcd

Browse files
committed
fix(webapp): allow JWT auth on POST /api/v1/sessions
The route was secret-key-only by design ("Customer's server owns session creation"). That holds for the customer browser path — `chat.createStartSessionAction` runs server-side and authorizes there. But the cli-v3 MCP `start_agent_chat` tool is itself a server-side surface (developer's CLI/IDE acting as their own server) and only holds a JWT minted from the user's PAT. Without JWT support here it can't create sessions, blocking the entire MCP agent toolkit. Add `allowJWT: true` and an `authorization` block requiring the `write:sessions` (or `admin`) super-scope. Resource scoping by `taskIdentifier` isn't possible at auth-resolve time — action routes don't pass `body` to the resource callback, and the task name only lives in the body — so the resource is a `sessions` wildcard and the super-scope does the gating. The JWT-issuer (cli-v3 MCP, customer servers wrapping a wider auth helper, etc.) decides which scopes to mint, which is where per-task narrowing lives. Verified end-to-end: `mcp__trigger__start_agent_chat` → `send_agent_message("pong")` → `send_agent_message("echo")` → `close_agent_chat` all succeed against local. Two assistant turns reuse the same runId (continuation in the idle window).
1 parent c69e939 commit 51b0fcd

1 file changed

Lines changed: 19 additions & 1 deletion

File tree

apps/webapp/app/routes/api.v1.sessions.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,31 @@ const { action } = createActionApiRoute(
9696
body: CreateSessionRequestBody,
9797
method: "POST",
9898
maxContentLength: 1024 * 32, // 32KB — metadata is the only thing that grows
99-
// Secret-key only. Customer's server (typically wrapping
99+
// Customer's server (typically wrapping
100100
// `chat.createStartSessionAction`) owns session creation so any
101101
// authorization decision (per-user/plan/quota) sits server-side
102102
// alongside whatever DB write the customer pairs with the create.
103103
// The session-scoped PAT returned in the response body is what the
104104
// browser uses thereafter against `.in/append`, `.out` SSE,
105105
// `end-and-continue`, etc.
106+
//
107+
// JWT is allowed when the caller holds an explicit `write:sessions` /
108+
// `admin` super-scope plus a `tasks:<taskIdentifier>` scope — gates
109+
// server-side surfaces like the cli-v3 MCP from creating sessions on
110+
// behalf of the developer without weakening the browser model.
111+
allowJWT: true,
112+
authorization: {
113+
// Resource scoping by `taskIdentifier` isn't possible at auth-resolve
114+
// time — action routes don't pass `body` to the resource callback,
115+
// and the task name only lives in the body. We require a `sessions`
116+
// resource scope (wildcard) and rely on `write:sessions` / `admin`
117+
// super-scopes to gate access. Per-task narrowing happens implicitly
118+
// because the JWT-issuer (e.g. cli-v3 MCP) decides which scopes to
119+
// request when minting the token.
120+
action: "write",
121+
resource: () => ({ sessions: "*" }),
122+
superScopes: ["write:sessions", "admin"],
123+
},
106124
corsStrategy: "all",
107125
},
108126
async ({ authentication, body }) => {

0 commit comments

Comments
 (0)