Skip to content

feat(mcp-gateway): durable per-client OAuth grants and Authorized Clients UI#4134

Open
pandemicsyn wants to merge 8 commits into
mainfrom
feat/mcp-gateway-authorized-clients
Open

feat(mcp-gateway): durable per-client OAuth grants and Authorized Clients UI#4134
pandemicsyn wants to merge 8 commits into
mainfrom
feat/mcp-gateway-authorized-clients

Conversation

@pandemicsyn

@pandemicsyn pandemicsyn commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens MCP Gateway OAuth client lifecycle by introducing durable per-client OAuth grants and a user-facing Authorized Clients tab on the gateway page.

Domain model. Adds mcp_gateway_oauth_grants with pending / active / revoked lifecycle and a unique partial index over (oauth_client_id, kilo_user_id, connect_resource_id, redirect_uri) for active bindings. Authorization requests, authorization codes, refresh tokens, pending provider authorizations, and audit events all gain a nullable oauth_grant_id foreign key so a single revocation cascades through every dependent artifact.

Token issuance and runtime enforcement. OAuth-client JWTs now embed token_source, oauth_grant_id, and client_id; derived connect tokens carry token_source=derived_connect. The Worker re-resolves the active grant on every authenticated runtime request (matching (grant_id, client_id, kilo_user_id, instance_id, connect_resource_id, config_version, exec_context, scopes, status=active)) before proxying upstream. Token exchange and refresh rotation lock the grant row inside the same transaction to close TOCTOU windows, and refresh paths surface invalid_grant immediately on revocation.

Lifecycle invalidation. Centralised revokeGrantIdsWithTx(tx, grantIds, reason) is now the single revocation primitive. It cascades to authorization requests, authorization codes, refresh tokens, pending provider authorizations, and writes audit events. Wired into:

  • User-initiated revoke from the Authorized Clients UI.
  • OAuth client soft delete and material metadata change (only on shrinking changes — additive redirect_uris / grant_types / response_types / scopes are preserved; redirect URIs are normalised before comparison).
  • Config disable, delete, material change, route rotation, and assignment removal.
  • User block (single + bulk) and GDPR soft delete.
  • Organization membership removal.

Provider OAuth callback ordering. The dynamic-provider callback path now persists the upstream provider grant before issuing the first-level authorization code, so a client can never receive a code without usable provider credentials. Pending grants are created at authorize time and only promoted to active during callback finalisation.

UI. The MCP Gateway page now uses tabs: Connections (existing) and Authorized clients (new). Each grant card shows the reported client name in quotes with an "unverified" tooltip on the shield, an inline Authorized to use <connection link> on behalf of <context> subline, a promoted "What this grants" callout for the broad mcp:access scope, and a <details> disclosure for the raw client ID and callback URI. Revoke confirmation renders the exact callback URI and client ID in code blocks. Per-org gateway pages filter the tab to that org's grants.

last_used_at honesty. A new touchOAuthGrantUsage helper updates last_used_at from the Worker runtime path through c.executionCtx.waitUntil() with a SQL-side 30-second debounce, so the displayed timestamp reflects actual MCP traffic rather than just token refresh cycles.

Shared invariants. executionContextsMatch now lives in @kilocode/mcp-gateway and is shared by web mint and Worker runtime; the Worker also enforces config_version to match the web token-service contract, so material config changes invalidate already-issued tokens.

Verification

  • [ ]

Visual Changes

Screenshot 2026-06-19 at 4 25 26 PM

Reviewer Notes

  • Migration packages/db/src/migrations/0166_sloppy_annihilus.sql adds mcp_gateway_oauth_grants plus nullable oauth_grant_id columns and FK constraints on five existing tables. Validation runs in-line; row counts on the affected tables are small (internal users only) so the lock window is negligible.
  • Provider callback finalisation is now atomic: provider-grant persistence and OAuth grant activation share one transaction, gated by a pending OAuth grant lock. Re-read provider-oauth-service.ts:handleProviderCallback and authorization-service.ts:completeProviderAuthorization if you remember the previous order.
  • Material client change revocation is intentionally additive-aware. Confirm oauth-client-service.ts:isAdditiveSuperset matches the intended blast radius on DCR re-registration.
  • last_used_at write amplification is bounded by a SQL last_used_at < NOW() - 30s OR last_used_at IS NULL predicate on the same row. Worst case is one write per active grant per 30s under heavy traffic.
  • Worker test mocks were updated to include touchOAuthGrantUsage since the unit harness has no c.executionCtx. The handler also tolerates missing executionCtx at runtime.
  • is_admin sidebar gate on the gateway entry is the deploy feature flag, not a permanent ACL. The Authorized clients tab inherits visibility from that gate today; the page is per-user already, so no follow-up is required when the flag opens up.

…ients UI

Introduces durable OAuth grants bound per client + user + connect resource
+ instance + redirect URI + execution context + scopes + config version,
with explicit pending/active/revoked lifecycle.

Authorization, token issuance, refresh, /userinfo, and Worker runtime now
require a non-revoked grant binding. Provider OAuth callbacks finalize
the provider grant before issuing the first-level authorization code so
clients never receive a code without usable provider credentials.

Adds a user-scoped Authorized Clients UI at /cloud/mcp-gateway/authorized-clients
that lists active grants, exposes per-grant revoke, and surfaces grant
metadata (callback URI, exact context, granted permissions, last use).

Lifecycle revocation cascades through one helper (revokeGrantIdsWithTx)
so authorization requests, codes, refresh tokens, and pending provider
authorizations are invalidated together. Material OAuth client metadata
changes, config disable/delete/material change, route rotation,
assignment removal, OAuth client deletion, GDPR delete, organization
membership removal, and user blocking all funnel through this helper.

OAuth client metadata revocation only fires on shrinking changes
(removed redirect URIs, scopes, grant types, or response types) and
normalizes redirect URIs to avoid mass-revoke on cosmetic re-registration.

Worker runtime grant validation also enforces config_version, matching
the web token-service binding contract; the executionContext comparison
helper now lives in @kilocode/mcp-gateway and is shared by both edges.
…y page

The MCP Gateway list page now has Connections and Authorized clients
tabs instead of a separate route. The Authorized Clients sidebar entry
is removed; users access their authorized clients alongside the
gateway connections, scoped to the current personal or organization
context.

mcpGatewayAuthorizationsRouter.listMine now accepts an optional
{ ownerScope, organizationId } filter so the org-scoped tab only
surfaces grants for that organization.
The card title now shows the reported client name in quotes (e.g.
"Kilo") with the client ID and "Reported name" provenance label
underneath. The shield icon stays in place but is now a tooltip
trigger explaining that the name is self-reported and unverified.
Falls back to "Unverified MCP client" when the client did not
declare a name during registration.
…t usage

Card hierarchy:
- Title row keeps the shield + reported name in quotes.
- New subline links the connection name (deep-links into the gateway
  config detail page) and shows the on-behalf-of context inline.
- Muted footer renders Authorized + Last used as a single tabular line.
- Promoted "What this grants" callout when the grant carries the broad
  mcp:access scope, so the consequential fact is the most visible one.
- Tucked the callback URI and full client ID behind a "Show technical
  details" disclosure to lower the visual noise floor.
- Replaced the Context column with the inline "on behalf of" copy.
- Added a focus ring to the unverified-shield tooltip trigger.

Backend:
- listMine now returns configId so the UI can build the connection link.
- New touchOAuthGrantUsage helper updates last_used_at from the Worker
  runtime path. The update is fired through c.executionCtx.waitUntil()
  on every authenticated MCP request, with a SQL-side 30s debounce so
  the same grant is not rewritten on every proxy hop.
@kilo-code-bot

kilo-code-bot Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/app/api/mcp-gateway/oauth/register/resource/[scope]/[ownerId]/[configId]/[routeKey]/route.ts 17 Route-param validation still bypasses the public registration rate limit for syntactically invalid probes.
apps/web/src/app/api/mcp-gateway/oauth/token/route.ts 62 The new no-store hardening does not cover thrown-error responses from the scoped token endpoint.

Fix these issues in Kilo Cloud

Files Reviewed (4 files)
  • apps/web/src/app/api/mcp-gateway/oauth/register/resource/[scope]/[ownerId]/[configId]/[routeKey]/route.test.ts - 0 issues
  • apps/web/src/app/api/mcp-gateway/oauth/register/resource/[scope]/[ownerId]/[configId]/[routeKey]/route.ts - 1 issue
  • apps/web/src/app/api/mcp-gateway/oauth/token/route.test.ts - 0 issues
  • apps/web/src/app/api/mcp-gateway/oauth/token/route.ts - 1 issue
Previous Review Summaries (4 snapshots, latest commit 3c4134a)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 3c4134a)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (100 files)
  • .specs/mcp-gateway-auth.md
  • CONTEXT.md
  • apps/mobile/src/app/(app)/agent-chat/model-picker.tsx
  • apps/mobile/src/components/agents/model-selector.tsx
  • apps/mobile/src/lib/free-model-data-disclosure.test.ts
  • apps/mobile/src/lib/free-model-data-disclosure.ts
  • apps/mobile/src/lib/hooks/use-available-models.ts
  • apps/storybook/stories/code-reviews/AnalyticsBreakdownBars.stories.tsx
  • apps/web/src/app/(app)/claw/components/SettingsTab.tsx
  • apps/web/src/app/(app)/claw/components/VersionPinCard.tsx
  • apps/web/src/app/(app)/claw/components/version-display.test.ts
  • apps/web/src/app/(app)/claw/hooks/useClawModelOptions.ts
  • apps/web/src/app/(app)/cloud/mcp-gateway/AuthorizedClientsContent.tsx
  • apps/web/src/app/(app)/cloud/mcp-gateway/McpGatewayListContent.tsx
  • apps/web/src/app/(app)/cloud/webhooks/[triggerId]/EditWebhookTriggerContent.tsx
  • apps/web/src/app/(app)/cloud/webhooks/new/CreateWebhookTriggerContent.tsx
  • apps/web/src/app/(app)/code-reviews/ReviewAgentPageClient.tsx
  • apps/web/src/app/(app)/gastown/[townId]/rigs/[rigId]/settings/RigSettingsPageClient.tsx
  • apps/web/src/app/(app)/gastown/[townId]/settings/TownSettingsPageClient.tsx
  • apps/web/src/app/(app)/gastown/onboarding/OnboardingStepModel.tsx
  • apps/web/src/app/(app)/organizations/[id]/code-reviews/ReviewAgentPageClient.tsx
  • apps/web/src/app/(app)/profile/page.tsx
  • apps/web/src/app/admin/api/auto-routing/benchmark-config/route.test.ts
  • apps/web/src/app/admin/api/backfills/block-blacklisted-domains/route.ts
  • apps/web/src/app/admin/auto-routing/BenchmarksSection.test.ts
  • apps/web/src/app/admin/auto-routing/BenchmarksSection.tsx
  • apps/web/src/app/api/auto-routing/mode/route.test.ts
  • apps/web/src/app/api/auto-routing/mode/route.ts
  • apps/web/src/app/api/internal/code-review-status/[reviewId]/route.test.ts
  • apps/web/src/app/api/internal/code-review-status/[reviewId]/route.ts
  • apps/web/src/app/api/mcp-gateway/oauth/authorize/route.test.ts
  • apps/web/src/app/api/mcp-gateway/oauth/authorize/route.ts
  • apps/web/src/app/api/openrouter/[...path]/route.ts
  • apps/web/src/components/app-builder/AppBuilderChat.tsx
  • apps/web/src/components/app-builder/AppBuilderLanding.tsx
  • apps/web/src/components/auto-routing/AutoRoutingModeCard.tsx
  • apps/web/src/components/cloud-agent-next/NewSessionPanel.tsx
  • apps/web/src/components/cloud-agent-next/hooks/useOrganizationModels.ts
  • apps/web/src/components/cloud-agent/CloudSessionsPage.tsx
  • apps/web/src/components/cloud-agent/hooks/useOrganizationModels.ts
  • apps/web/src/components/cloud-agent/profile-editor/KiloCommandsTab.tsx
  • apps/web/src/components/cloud-agent/profile-editor/ProfileAgentsTab.tsx
  • apps/web/src/components/code-reviews/analytics/AnalyticsBreakdownBars.tsx
  • apps/web/src/components/code-reviews/analytics/AnalyticsTables.tsx
  • apps/web/src/components/code-reviews/analytics/CodeReviewAnalyticsPanel.tsx
  • apps/web/src/components/integrations/DiscordIntegrationDetails.tsx
  • apps/web/src/components/integrations/GitHubIntegrationDetails.tsx
  • apps/web/src/components/integrations/LinearIntegrationDetails.tsx
  • apps/web/src/components/integrations/SlackIntegrationDetails.tsx
  • apps/web/src/components/organizations/byok/BYOKKeysManager.tsx
  • apps/web/src/components/organizations/providers-and-models/OrganizationProvidersAndModelsPage.tsx
  • apps/web/src/components/shared/ModelCombobox.tsx
  • apps/web/src/components/shared/free-model-data-disclosure.test.ts
  • apps/web/src/components/shared/free-model-data-disclosure.ts
  • apps/web/src/emails/codeReviewDisabled.html
  • apps/web/src/lib/abuse/bulkBlock.ts
  • apps/web/src/lib/agent-config/core/types.ts
  • apps/web/src/lib/agent-config/db/agent-configs.ts
  • apps/web/src/lib/ai-gateway/auto-routing-admin-client.ts
  • apps/web/src/lib/ai-gateway/auto-routing-benchmark-admin-client.test.ts
  • apps/web/src/lib/ai-gateway/auto-routing-decision.test.ts
  • apps/web/src/lib/ai-gateway/auto-routing-decision.ts
  • apps/web/src/lib/ai-gateway/handleRequestLogging.ts
  • apps/web/src/lib/ai-gateway/o11y/stream-lifecycle.server.test.ts
  • apps/web/src/lib/ai-gateway/o11y/stream-lifecycle.server.ts
  • apps/web/src/lib/code-reviews/action-required-shared.ts
  • apps/web/src/lib/code-reviews/action-required.test.ts
  • apps/web/src/lib/code-reviews/action-required.ts
  • apps/web/src/lib/code-reviews/analytics/contracts.test.ts
  • apps/web/src/lib/code-reviews/analytics/contracts.ts
  • apps/web/src/lib/code-reviews/analytics/db.test.ts
  • apps/web/src/lib/code-reviews/analytics/db.ts
  • apps/web/src/lib/code-reviews/analytics/settings.test.ts
  • apps/web/src/lib/code-reviews/analytics/settings.ts
  • apps/web/src/lib/code-reviews/db/code-reviews.test.ts
  • apps/web/src/lib/code-reviews/db/code-reviews.ts
  • apps/web/src/lib/code-reviews/dispatch/dispatch-pending-reviews.test.ts
  • apps/web/src/lib/code-reviews/dispatch/dispatch-pending-reviews.ts
  • apps/web/src/lib/code-reviews/prompts/default-prompt-template-gitlab.json
  • apps/web/src/lib/code-reviews/prompts/default-prompt-template.json
  • apps/web/src/lib/code-reviews/prompts/generate-prompt.test.ts
  • apps/web/src/lib/code-reviews/prompts/generate-prompt.ts
  • apps/web/src/lib/code-reviews/prompts/platform-helpers.ts
  • apps/web/src/lib/code-reviews/prompts/repository-review-instructions.ts
  • apps/web/src/lib/code-reviews/triggers/prepare-review-payload.test.ts
  • apps/web/src/lib/code-reviews/triggers/prepare-review-payload.ts
  • apps/web/src/lib/kilo-pass/cancel-and-refund.ts
  • apps/web/src/lib/kilo-pass/stripe-handlers-invoice-paid.ts
  • apps/web/src/lib/mcp-gateway/audit-service.ts
  • apps/web/src/lib/mcp-gateway/authorization-service.ts
  • apps/web/src/lib/mcp-gateway/blocking-service.ts
  • apps/web/src/lib/mcp-gateway/config-service.ts
  • apps/web/src/lib/mcp-gateway/grant-service.ts
  • apps/web/src/lib/mcp-gateway/lifecycle-service.ts
  • apps/web/src/lib/mcp-gateway/oauth-client-service.ts
  • apps/web/src/lib/mcp-gateway/oauth-flow.test.ts
  • apps/web/src/lib/mcp-gateway/oauth-grant-service.ts
  • apps/web/src/lib/mcp-gateway/provider-oauth-service.ts
  • apps/web/src/lib/mcp-gateway/services.ts
  • apps/web/src/lib/mcp-gateway/token-service.ts

Previous review (commit ec6b3b9)

Status: 1 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/db/src/schema.ts 8408 Active OAuth grant uniqueness omits owner context, so one authorization can revoke another across personal and organization scopes.
Files Reviewed (16 files)
  • .specs/mcp-gateway-auth.md - 0 issues
  • apps/web/src/app/admin/api/backfills/block-blacklisted-domains/route.ts - 0 issues
  • apps/web/src/app/api/mcp-gateway/oauth/authorize/route.test.ts - 0 issues
  • apps/web/src/app/api/mcp-gateway/oauth/authorize/route.ts - 0 issues
  • apps/web/src/lib/abuse/bulkBlock.ts - 0 issues
  • apps/web/src/lib/kilo-pass/cancel-and-refund.ts - 0 issues
  • apps/web/src/lib/kilo-pass/stripe-handlers-invoice-paid.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/blocking-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/oauth-client-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/oauth-flow.test.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/token-service.ts - previous /userinfo audience-validation warning appears fixed
  • apps/web/src/lib/stripe/disputes.ts - 0 issues
  • apps/web/src/lib/stytch.ts - 0 issues
  • apps/web/src/routers/admin-router.ts - 0 issues
  • apps/web/src/routers/mcp-gateway-authorizations-router.test.ts - previous organization-membership warning now has regression coverage
  • packages/db/src/schema.ts - carried-forward warning (unchanged this round)

Fix these issues in Kilo Cloud

Previous review (commit c2cbf6d)

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/db/src/schema.ts 8265 Active OAuth grant uniqueness omits owner/execution context, so one authorization can revoke another across personal and organization scopes.
apps/web/src/routers/mcp-gateway-authorizations-router.ts 3140 Authorized-clients org filtering trusts a caller-supplied organizationId without verifying membership/current org scope.
apps/web/src/lib/mcp-gateway/token-service.ts 156 /userinfo token verification skips audience validation, allowing any profile-scoped gateway JWT to be replayed there.
Files Reviewed (32 files)
  • .specs/mcp-gateway-auth.md - 0 issues
  • apps/web/src/app/(app)/cloud/mcp-gateway/AuthorizedClientsContent.tsx - 0 issues
  • apps/web/src/app/(app)/cloud/mcp-gateway/McpGatewayListContent.tsx - 0 issues
  • apps/web/src/lib/abuse/bulkBlock.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/audit-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/authorization-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/config-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/grant-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/lifecycle-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/oauth-client-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/oauth-flow.test.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/oauth-grant-service.ts - 0 issues beyond the schema-level uniqueness issue above
  • apps/web/src/lib/mcp-gateway/provider-oauth-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/services.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/token-service.ts - 1 issue
  • apps/web/src/lib/user/index.test.ts - 0 issues
  • apps/web/src/routers/admin-router.ts - 0 issues
  • apps/web/src/routers/mcp-gateway-authorizations-router.test.ts - 0 issues
  • apps/web/src/routers/mcp-gateway-authorizations-router.ts - 1 issue
  • apps/web/src/routers/root-router.ts - 0 issues
  • packages/db/src/migrations/0166_sloppy_annihilus.sql - 0 issues
  • packages/db/src/migrations/meta/0166_snapshot.json - skipped generated file
  • packages/db/src/migrations/meta/_journal.json - skipped generated file
  • packages/db/src/schema-types.ts - 0 issues
  • packages/db/src/schema.ts - 1 issue
  • packages/mcp-gateway/src/index.test.ts - 0 issues
  • packages/mcp-gateway/src/index.ts - 0 issues
  • packages/mcp-gateway/src/schemas.ts - 0 issues
  • packages/mcp-gateway/src/types.ts - 0 issues
  • services/mcp-gateway/src/db/runtime-repository.ts - 0 issues
  • services/mcp-gateway/src/handlers/connect.handler.ts - 0 issues
  • services/mcp-gateway/src/lib/cleanup.ts - 0 issues
  • services/mcp-gateway/src/mcp-gateway.worker.test.ts - 0 issues

Fix these issues in Kilo Cloud

Previous review (commit fb99d20)

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/db/src/schema.ts 8265 Active OAuth grant uniqueness omits owner/execution context, so one authorization can revoke another across personal and organization scopes.
apps/web/src/routers/mcp-gateway-authorizations-router.ts 3140 Authorized-clients org filtering trusts a caller-supplied organizationId without verifying membership/current org scope.
apps/web/src/lib/mcp-gateway/token-service.ts 156 /userinfo token verification skips audience validation, allowing any profile-scoped gateway JWT to be replayed there.
Files Reviewed (32 files)
  • .specs/mcp-gateway-auth.md - 0 issues
  • apps/web/src/app/(app)/cloud/mcp-gateway/AuthorizedClientsContent.tsx - 0 issues
  • apps/web/src/app/(app)/cloud/mcp-gateway/McpGatewayListContent.tsx - 0 issues
  • apps/web/src/lib/abuse/bulkBlock.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/audit-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/authorization-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/config-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/grant-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/lifecycle-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/oauth-client-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/oauth-flow.test.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/oauth-grant-service.ts - 0 issues beyond the schema-level uniqueness issue above
  • apps/web/src/lib/mcp-gateway/provider-oauth-service.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/services.ts - 0 issues
  • apps/web/src/lib/mcp-gateway/token-service.ts - 1 issue
  • apps/web/src/lib/user/index.test.ts - 0 issues
  • apps/web/src/routers/admin-router.ts - 0 issues
  • apps/web/src/routers/mcp-gateway-authorizations-router.test.ts - 0 issues
  • apps/web/src/routers/mcp-gateway-authorizations-router.ts - 1 issue
  • apps/web/src/routers/root-router.ts - 0 issues
  • packages/db/src/migrations/0166_sloppy_annihilus.sql - 0 issues
  • packages/db/src/migrations/meta/0166_snapshot.json - skipped generated file
  • packages/db/src/migrations/meta/_journal.json - skipped generated file
  • packages/db/src/schema-types.ts - 0 issues
  • packages/db/src/schema.ts - 1 issue
  • packages/mcp-gateway/src/index.test.ts - 0 issues
  • packages/mcp-gateway/src/index.ts - 0 issues
  • packages/mcp-gateway/src/schemas.ts - 0 issues
  • packages/mcp-gateway/src/types.ts - 0 issues
  • services/mcp-gateway/src/db/runtime-repository.ts - 0 issues
  • services/mcp-gateway/src/handlers/connect.handler.ts - 0 issues
  • services/mcp-gateway/src/lib/cleanup.ts - 0 issues
  • services/mcp-gateway/src/mcp-gateway.worker.test.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by gpt-5.4-20260305 · Input: 85.5K · Output: 10K · Cached: 446.5K

Review guidance: REVIEW.md from base branch main

Comment thread packages/db/src/schema.ts
},
table => [
uniqueIndex('UQ_mcp_gateway_oauth_grants_active_binding')
.on(table.oauth_client_id, table.kilo_user_id, table.connect_resource_id, table.redirect_uri)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Active grant uniqueness ignores owner context

The new unique binding only keys on (oauth_client_id, kilo_user_id, connect_resource_id, redirect_uri), so the same user cannot hold separate active grants for the same client/resource pair in different owner contexts. createOrReuseGrant() relies on that same lookup, which means authorizing an org-owned connection after authorizing a personal one can revoke and replace the first grant even though the spec says a grant is bound to one exact execution context and owner tuple.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed and not warranted. connect_resource_id is the PK of mcp_gateway_connect_resources, and a personal config and an org config produce distinct connect resources with distinct UUIDs even for the same MCP client. The active-binding unique index over (oauth_client_id, kilo_user_id, connect_resource_id, redirect_uri) therefore cannot conflate personal and organization grants. Cross-context revocation in createOrReuseGrant() only happens when the existing grant's (instance_id, config_version, scopes, execution_context) already match the new request, which by definition shares the same owner context as well.

Comment thread apps/web/src/routers/mcp-gateway-authorizations-router.ts
Regenerated migration as 0167_wealthy_eternity.sql against current schema after dropping the branch-local 0166_sloppy_annihilus.sql per AGENTS.md migration-conflict procedure.
…info audience

- listMine now verifies organization membership before honoring an
  organizationId filter so an unrelated org cannot be passed to scope
  the listing.
- userInfo now requires the bearer token's audience to start with the
  gateway base URL, so access tokens minted for resources outside this
  gateway cannot be replayed at the userinfo endpoint.
@pandemicsyn

Copy link
Copy Markdown
Contributor Author

Addressed bot review feedback in c2cbf6d + d8de963 (merge):

  • /userinfo audience validation (token-service.ts:156): Fixed in c2cbf6d. userInfo() now requires the bearer token's aud to start with gatewayBaseUrl + /mcp-connect/, so access tokens minted by other issuers (or non-MCP audiences within this gateway) cannot be replayed at the userinfo endpoint.
  • Organization-scoped listMine (mcp-gateway-authorizations-router.ts:43): Fixed in c2cbf6d. The router now verifies isOrganizationMember before honoring the org filter and throws FORBIDDEN on mismatch. Replied inline.
  • Active grant uniqueness (schema.ts:8265): Not warranted, replied inline. connect_resource_id already encodes owner context (personal vs org configs produce distinct connect resources with distinct UUIDs), so the index cannot conflate grants across owner scopes.

Also merged origin/main and regenerated the migration as 0167_wealthy_eternity.sql per AGENTS.md migration-conflict procedure (the previous 0166_sloppy_annihilus.sql collided with main's 0166_open_xorn.sql).

@pandemicsyn pandemicsyn force-pushed the feat/mcp-gateway-authorized-clients branch from ec6b3b9 to 3c4134a Compare June 20, 2026 17:02
@pandemicsyn

pandemicsyn commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

Addressed follow-up PR3 findings in 3c4134a:

  • Code-only clients no longer receive latent refresh tokens; adding/removing refresh_token capability is now material and revokes existing grants/codes/tokens.
  • Redirect URI revocation now uses exact raw URI strings, so /callback -> /callback/ revokes the old binding.
  • Added a shared blocked-user grant revocation helper and wired it into dispute, Kilo Pass, Stytch autoban, and blacklist backfill block paths.
  • Explicit consent denial now records a sanitized authorization_denied audit event without creating authorization/provider/grant/token state.

@pandemicsyn

Copy link
Copy Markdown
Contributor Author

Follow-up hardening in db91329:

  • OAuth token responses now emit Cache-Control: no-store and Pragma: no-cache on success and error paths.
  • Resource registration now consumes the public registration rate-limit bucket before resolving the route, so invalid-route probing cannot bypass the bucket and force route lookups first.

@@ -15,8 +15,8 @@ export async function POST(
try {
const services = createGatewayServices();
const route = parseScopedRouteParams(await params);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Route-param validation still bypasses the public registration bucket

parseScopedRouteParams(await params) still runs before consumeRegistrationRateLimit(), so requests with syntactically invalid scope/ownerId/configId/routeKey return 400 without incrementing the unauthenticated registration rate limit. If the goal is to throttle every public registration probe, the rate-limit check needs to happen before route-param parsing as well.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

return await exchangeToken(request);
} catch (error) {
return gatewayErrorResponse(error);
return withTokenResponseHeaders(gatewayErrorResponse(error));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Scoped token errors still miss the new no-store headers

This catch only hardens /api/mcp-gateway/oauth/token. /api/mcp-gateway/oauth/token/[scope]/... still has its own catch that returns gatewayErrorResponse(error) directly, so any thrown GatewayError there remains cacheable. If the intent is "success and error paths" for token endpoints, the header application needs to be shared with the scoped route as well.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant