You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #216 (PR-2c) landed the OAuth install/callback handshake + the B24OAuth factory + dispatcher wiring. After it merges an operator can flip NUXT_BITRIX24_OAUTH_ENABLED=true, complete /api/oauth/install → /api/oauth/callback, and receive a Bearer — but /mcp doesn't accept that Bearer yet. It still authenticates with NUXT_MCP_AUTH_TOKEN. The callback HTML page warns the user about this explicitly ("⚠ Not active yet"), but the wire-up is the load-bearing missing piece of the whole OAuth stack.
This issue tracks that final connection. Split out of #216 to keep that PR's review focused (the middleware touches defineMcpHandler, a different architectural plane).
Scope
MCP Bearer middleware. Extend the MCP handler (server/mcp/index.ts / wherever defineMcpHandler is registered) with a middleware hook that:
extracts the Authorization: Bearer <token> header,
hashes it (sha256-<hex>) and looks it up via findByBearerHash,
on miss / revoked / orphan, returns 401 with the §11 mcp.auth.deny.* event + errorCode.
Flag-gated. When NUXT_BITRIX24_OAUTH_ENABLED=false, the middleware is a no-op and /mcp keeps using NUXT_MCP_AUTH_TOKEN exactly as today (webhook-only forks unchanged).
§11 events (already in the taxonomy, marked "deferred" — flip them to live):
mcp.auth.deny.bearer-unknown
mcp.auth.deny.bearer-revoked
mcp.auth.deny.bearer-orphan
getRequestId() becomes reachable — this is the first production caller. Add the test that proves getRequestId() returns a 32-char hex inside a real middleware-wrapped request.
Acceptance
/mcp with a valid minted Bearer (flag on) → tenant context populated, tool call resolves through useBitrix24OAuth.
/mcp with an unknown / revoked Bearer → 401 + the matching mcp.auth.deny.* event.
/mcp with NUXT_BITRIX24_OAUTH_ENABLED=false → unchanged (NUXT_MCP_AUTH_TOKEN path).
requestId populated unconditionally; getRequestId() returns it; the throw path is unreachable in production.
Update OAUTH-DESIGN.md §11: flip the mcp.auth.deny.* block from "(deferred)" to live; update the §10 rollout table row.
Update the callback HTML page: remove the "⚠ Not active yet" warning once the middleware is live.
Blocks
This is the gate for the OAuth flow being end-to-end usable. The full stack (#58 design, #209 scaffold, #210 token store, #213 tool swap, #216 install/callback) is functionally inert for end users until this lands.
Spawned from PR #216 round-3 review (CTO agent — flagged the missing tracking issue).
Context
PR #216 (PR-2c) landed the OAuth install/callback handshake + the
B24OAuthfactory + dispatcher wiring. After it merges an operator can flipNUXT_BITRIX24_OAUTH_ENABLED=true, complete/api/oauth/install → /api/oauth/callback, and receive a Bearer — but/mcpdoesn't accept that Bearer yet. It still authenticates withNUXT_MCP_AUTH_TOKEN. The callback HTML page warns the user about this explicitly ("⚠ Not active yet"), but the wire-up is the load-bearing missing piece of the whole OAuth stack.This issue tracks that final connection. Split out of #216 to keep that PR's review focused (the middleware touches
defineMcpHandler, a different architectural plane).Scope
MCP Bearer middleware. Extend the MCP handler (
server/mcp/index.ts/ whereverdefineMcpHandleris registered) with amiddlewarehook that:Authorization: Bearer <token>header,sha256-<hex>) and looks it up viafindByBearerHash,runWithTenant({ memberId, userId, requestId }, () => next())(therequestIdis a fresh 16-byte hex generated here — this is thegetRequestId()consumer PR-2c: requestId — populate unconditionally + getRequestId() throwing helper #214 added),mcp.auth.deny.*event + errorCode.Flag-gated. When
NUXT_BITRIX24_OAUTH_ENABLED=false, the middleware is a no-op and/mcpkeeps usingNUXT_MCP_AUTH_TOKENexactly as today (webhook-only forks unchanged).§11 events (already in the taxonomy, marked "deferred" — flip them to live):
mcp.auth.deny.bearer-unknownmcp.auth.deny.bearer-revokedmcp.auth.deny.bearer-orphangetRequestId()becomes reachable — this is the first production caller. Add the test that provesgetRequestId()returns a 32-char hex inside a real middleware-wrapped request.Acceptance
/mcpwith a valid minted Bearer (flag on) → tenant context populated, tool call resolves throughuseBitrix24OAuth./mcpwith an unknown / revoked Bearer → 401 + the matchingmcp.auth.deny.*event./mcpwithNUXT_BITRIX24_OAUTH_ENABLED=false→ unchanged (NUXT_MCP_AUTH_TOKENpath).requestIdpopulated unconditionally;getRequestId()returns it; the throw path is unreachable in production.OAUTH-DESIGN.md §11: flip themcp.auth.deny.*block from "(deferred)" to live; update the §10 rollout table row.Blocks
This is the gate for the OAuth flow being end-to-end usable. The full stack (#58 design, #209 scaffold, #210 token store, #213 tool swap, #216 install/callback) is functionally inert for end users until this lands.
Spawned from PR #216 round-3 review (CTO agent — flagged the missing tracking issue).