Skip to content

Commit 8e83df5

Browse files
author
Lasim
committed
feat(all): implement OAuth pending flows to prevent orphaned installations
Replaced immediate installation creation with two-phase OAuth flow: - New oauthPendingFlows table stores temporary OAuth state (10-min expiry) - Authorize endpoint creates flow instead of installation with oauth_pending=true - Callback endpoint creates installation only after successful OAuth completion - Cleanup cron job runs every 3 minutes to delete expired flows Backend changes: - Add oauthPendingFlows table with indexes for state and expiration - Update authorize.ts to create flows with flow_id instead of installation_id - Update callback.ts route from /installations/:installationId/oauth/callback to /oauth/callback/:flowId - Add CleanupExpiredOAuthPendingFlowsWorker and cron job - Update response schemas to use flow_id instead of installation_id Frontend changes: - Update mcpInstallationService.ts to handle flow_id response - Update McpServerInstallWizard.vue to use flow_id for completion Database migration: 0010_young_jimmy_woo.sql Fixes issue where closing OAuth popup created orphaned installations. Installations now only exist after successful OAuth authorization.
1 parent a3ce748 commit 8e83df5

File tree

16 files changed

+6411
-186
lines changed

16 files changed

+6411
-186
lines changed

services/backend/api-spec.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26096,9 +26096,9 @@
2609626096
"schema": {
2609726097
"type": "object",
2609826098
"properties": {
26099-
"installation_id": {
26099+
"flow_id": {
2610026100
"type": "string",
26101-
"description": "Unique installation ID for the pending OAuth installation"
26101+
"description": "Unique flow ID for the pending OAuth flow"
2610226102
},
2610326103
"authorization_url": {
2610426104
"type": "string",
@@ -26116,7 +26116,7 @@
2611626116
}
2611726117
},
2611826118
"required": [
26119-
"installation_id",
26119+
"flow_id",
2612026120
"authorization_url",
2612126121
"requires_authorization",
2612226122
"expires_at"
@@ -26259,7 +26259,7 @@
2625926259
}
2626026260
}
2626126261
},
26262-
"/api/teams/{teamId}/mcp/installations/{installationId}/oauth/callback": {
26262+
"/api/teams/{teamId}/mcp/oauth/callback/{flowId}": {
2626326263
"get": {
2626426264
"tags": [
2626526265
"MCP Installations",
@@ -26311,17 +26311,17 @@
2631126311
"in": "path",
2631226312
"name": "teamId",
2631326313
"required": true,
26314-
"description": "Team ID that owns the installation"
26314+
"description": "Team ID"
2631526315
},
2631626316
{
2631726317
"schema": {
2631826318
"type": "string",
2631926319
"minLength": 1
2632026320
},
2632126321
"in": "path",
26322-
"name": "installationId",
26322+
"name": "flowId",
2632326323
"required": true,
26324-
"description": "Installation ID"
26324+
"description": "OAuth flow ID"
2632526325
}
2632626326
],
2632726327
"responses": {

services/backend/api-spec.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18506,9 +18506,9 @@ paths:
1850618506
schema:
1850718507
type: object
1850818508
properties:
18509-
installation_id:
18509+
flow_id:
1851018510
type: string
18511-
description: Unique installation ID for the pending OAuth installation
18511+
description: Unique flow ID for the pending OAuth flow
1851218512
authorization_url:
1851318513
type: string
1851418514
format: uri
@@ -18522,7 +18522,7 @@ paths:
1852218522
format: date-time
1852318523
description: ISO 8601 timestamp when the OAuth state expires
1852418524
required:
18525-
- installation_id
18525+
- flow_id
1852618526
- authorization_url
1852718527
- requires_authorization
1852818528
- expires_at
@@ -18617,7 +18617,7 @@ paths:
1861718617
- success
1861818618
- error
1861918619
description: Internal Server Error
18620-
/api/teams/{teamId}/mcp/installations/{installationId}/oauth/callback:
18620+
/api/teams/{teamId}/mcp/oauth/callback/{flowId}:
1862118621
get:
1862218622
tags:
1862318623
- MCP Installations
@@ -18654,14 +18654,14 @@ paths:
1865418654
in: path
1865518655
name: teamId
1865618656
required: true
18657-
description: Team ID that owns the installation
18657+
description: Team ID
1865818658
- schema:
1865918659
type: string
1866018660
minLength: 1
1866118661
in: path
18662-
name: installationId
18662+
name: flowId
1866318663
required: true
18664-
description: Installation ID
18664+
description: OAuth flow ID
1866518665
responses:
1866618666
"200":
1866718667
description: Default Response
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
CREATE TABLE "oauthPendingFlows" (
2+
"id" text PRIMARY KEY NOT NULL,
3+
"team_id" text NOT NULL,
4+
"server_id" text NOT NULL,
5+
"created_by" text NOT NULL,
6+
"oauth_state" text NOT NULL,
7+
"oauth_code_verifier" text NOT NULL,
8+
"oauth_client_id" text NOT NULL,
9+
"oauth_client_secret" text,
10+
"oauth_provider_id" text,
11+
"oauth_token_endpoint" text NOT NULL,
12+
"oauth_token_endpoint_auth_method" text NOT NULL,
13+
"installation_name" text NOT NULL,
14+
"installation_type" text DEFAULT 'global' NOT NULL,
15+
"team_config" text,
16+
"expires_at" timestamp with time zone NOT NULL,
17+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
18+
);
19+
--> statement-breakpoint
20+
ALTER TABLE "oauthPendingFlows" ADD CONSTRAINT "oauthPendingFlows_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
21+
ALTER TABLE "oauthPendingFlows" ADD CONSTRAINT "oauthPendingFlows_server_id_mcpServers_id_fk" FOREIGN KEY ("server_id") REFERENCES "public"."mcpServers"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
22+
ALTER TABLE "oauthPendingFlows" ADD CONSTRAINT "oauthPendingFlows_created_by_authUser_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."authUser"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
23+
ALTER TABLE "oauthPendingFlows" ADD CONSTRAINT "oauthPendingFlows_oauth_provider_id_mcpOauthProviders_id_fk" FOREIGN KEY ("oauth_provider_id") REFERENCES "public"."mcpOauthProviders"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
24+
CREATE INDEX "oauth_pending_flows_state_idx" ON "oauthPendingFlows" USING btree ("oauth_state");--> statement-breakpoint
25+
CREATE INDEX "oauth_pending_flows_expires_at_idx" ON "oauthPendingFlows" USING btree ("expires_at");--> statement-breakpoint
26+
CREATE INDEX "oauth_pending_flows_team_server_idx" ON "oauthPendingFlows" USING btree ("team_id","server_id");--> statement-breakpoint
27+
CREATE INDEX "oauth_pending_flows_created_by_idx" ON "oauthPendingFlows" USING btree ("created_by");

0 commit comments

Comments
 (0)