Skip to content

refactor: merge oauth_tokens/oauth_user_tokens into unified mcp_oauth_tokens table - #5708

Merged
Pratham-Mishra04 merged 1 commit into
devfrom
07-28-feat_unify_shared_and_per-user_mcp_oauth_token_storage_into_tablemcpoauthtoken
Aug 8, 2026
Merged

refactor: merge oauth_tokens/oauth_user_tokens into unified mcp_oauth_tokens table#5708
Pratham-Mishra04 merged 1 commit into
devfrom
07-28-feat_unify_shared_and_per-user_mcp_oauth_token_storage_into_tablemcpoauthtoken

Conversation

@Pratham-Mishra04

Copy link
Copy Markdown
Collaborator

Summary

Merges the two legacy MCP OAuth token tables (oauth_tokens for shared client credentials and oauth_user_tokens for per-identity credentials) into a single unified table, mcp_oauth_tokens, distinguished by an auth_mode column (shared | user | vk | session). This eliminates the split storage model, closes a long-standing orphan leak where deleting an MCP client never cleaned up its shared oauth_tokens row or its oauth_configs row, and adds defense-in-depth guards so per-user code paths can never read, mutate, or delete a shared credential and vice versa.

Changes

  • Introduces TableMCPOauthToken mapped to mcp_oauth_tokens, carrying every column both predecessor tables held plus auth_mode and status. TableOauthToken and TableOauthUserToken are retained as deprecated stubs so historical migrations keep compiling; both will be dropped in the next major version.
  • Adds migration merge_oauth_token_tables as the final registered step. It creates mcp_oauth_tokens fresh, backfills shared rows from oauth_tokens (deriving auth_mode='shared', mcp_client_id, and oauth_config_id inline via subselect), copies per-identity rows from oauth_user_tokens field-for-field, dedupes any shared-mode mcp_client_id collisions introduced by the pre-existing orphan leak, then creates four partial unique indexes (one per auth_mode). Neither legacy table is dropped.
  • All ConfigStore interface methods and their RDBConfigStore implementations are updated to operate on TableMCPOauthToken. Per-user-scoped methods (GetOauthUserTokenByID, UpdateOauthUserToken, DeleteOauthUserToken, MarkOauthUserTokenNeedsReauthByID, DeleteOrphanedOauthUserTokens, ListOauthUserTokens) now filter on auth_mode IN ('user','vk','session') so a shared row can never be reached through a per-user endpoint.
  • GetExpiringOauthTokens gains an explicit auth_mode = 'shared' filter so the TokenRefreshWorker continues to handle only shared credentials; per-user proactive refresh remains a deliberate later change.
  • DeleteMCPClientConfig now deletes all mcp_oauth_tokens rows for the client (shared and per-identity alike) in one pass and explicitly deletes the client's oauth_configs row, closing the orphan leak that previously left both untouched.
  • DeleteVirtualKey and reconcileVKDirectTokensDB are updated to target mcp_oauth_tokens with auth_mode scoping.
  • CompleteOAuthFlow populates AuthMode='shared', OauthConfigID, Status='active', and attempts to derive MCPClientID via GetMCPClientByOauthConfigID before creating the token record.
  • Adds migrations_perf_test.go with TestMigrationMergeOauthTokenTablesPerf, gated behind BIFROST_RUN_MIGRATION_PERF_TESTS=1, which seeds ~1,000,000 rows across the pre-merge tables and measures the migration's wall-clock cost against both SQLite and Postgres.
  • perUserOauthAuthModes is introduced as a package-level slice centralizing the set of non-shared auth modes used by all scoped queries.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./framework/configstore/... ./framework/oauth2/... ./transports/bifrost-http/...

# To run the migration performance test against ~1,000,000 seeded rows:
BIFROST_RUN_MIGRATION_PERF_TESTS=1 go test ./framework/configstore/... -run TestMigrationMergeOauthTokenTablesPerf -v -timeout 30m

Validate that:

  • A fresh install creates mcp_oauth_tokens and leaves oauth_tokens/oauth_user_tokens empty but present.
  • An upgrade from a pre-merge schema runs the migration without error, copies all rows, and leaves the legacy tables untouched.
  • Deleting an OAuth MCP client removes its mcp_oauth_tokens rows and its oauth_configs row.
  • The sessions UI lists only per-identity rows (no auth_mode='shared' rows appear).
  • Token refresh continues to work for shared-mode clients.

Breaking changes

  • Yes
  • No

The ConfigStore interface signatures for all OAuth token methods now use *tables.TableMCPOauthToken instead of *tables.TableOauthToken or *tables.TableOauthUserToken. Any external implementation of ConfigStore must be updated. The underlying storage moves from oauth_tokens/oauth_user_tokens to mcp_oauth_tokens; the migration handles existing data automatically, but rollback drops mcp_oauth_tokens entirely and restores reads/writes to the legacy tables.

Security considerations

  • Per-user-scoped store methods now explicitly filter auth_mode IN ('user','vk','session'), preventing a caller-supplied token ID from resolving to a shared credential through endpoints intended only for per-identity credentials (e.g. POST /api/mcp/sessions/{id}/reauth, DELETE /api/mcp/sessions/{id}).
  • UpdateOauthUserToken rejects rows whose auth_mode is not one of the per-identity modes, preventing silent rewrites of shared credentials through the per-user API.
  • The orphan leak in DeleteMCPClientConfig (shared oauth_tokens row and oauth_configs row never cleaned up on client deletion) is closed.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Pratham-Mishra04 commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Unified storage for shared and identity-specific OAuth credentials.
    • Added migration support for existing credentials, including deduplication and rollback safety.
    • Improved OAuth completion and encryption across credential types.
  • Bug Fixes

    • Prevented shared credentials from appearing in or being modified through identity-specific operations.
    • Improved credential cleanup, expiration handling, and ownership filtering.
    • Added safeguards against duplicate credentials during migration.

Walkthrough

OAuth token storage is consolidated into mcp_oauth_tokens. A migration merges legacy records. Application interfaces and OAuth flows use TableMCPOauthToken. Per-user operations enforce auth-mode filtering. Encryption, session handling, and tests use the consolidated model.

Changes

OAuth token storage consolidation

Layer / File(s) Summary
Merged token schema and migration
framework/configstore/tables/mcpoauth2.go, framework/configstore/migrations.go, tests/scripts/migration-checker/main.go
Adds TableMCPOauthToken, registers the merge migration, backfills legacy records, deduplicates shared rows, and adds mode-specific indexes.
Migration performance validation
framework/configstore/migrations_perf_test.go
Adds gated SQLite and Postgres performance coverage with batched legacy data seeding and migrated row-count validation.
Persistence contracts and auth-mode filtering
framework/configstore/store.go, framework/configstore/rdb.go, framework/configstore/*_test.go
Updates token APIs and RDB operations to the merged model. Per-user access is limited to non-shared modes. Cleanup, refresh, deletion, and VK reconciliation use merged token rows.
OAuth completion and token encryption
framework/oauth2/main.go, framework/configstore/encryption.go, framework/configstore/encryption_test.go, framework/oauth2/sync_test.go
Persists shared and per-user tokens through the consolidated model and updates encryption and expiry coverage.
Session and adapter integration
transports/bifrost-http/handlers/mcpsessions.go, transports/bifrost-http/lib/config_test.go
Updates MCP session authorization, identity, binding-key, wire-row, and mock-store handling to use consolidated token records.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OAuthFlow
  participant ConfigStore
  participant mcp_oauth_tokens
  participant MCPHTTP
  OAuthFlow->>ConfigStore: create consolidated OAuth token
  ConfigStore->>mcp_oauth_tokens: persist shared or per-user row
  MCPHTTP->>ConfigStore: load authorized per-user token
  ConfigStore->>mcp_oauth_tokens: filter by auth mode and identity
  mcp_oauth_tokens-->>MCPHTTP: return authorized token row
Loading

Possibly related PRs

Suggested reviewers: akshaydeo, bearts, danpiths

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the primary change: merging legacy OAuth token tables into the unified mcp_oauth_tokens table.
Description check ✅ Passed The description covers the migration, code changes, testing, breaking changes, security impact, affected areas, and checklist; only related issues are not provided.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 07-28-feat_unify_shared_and_per-user_mcp_oauth_token_storage_into_tablemcpoauthtoken

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

♻️ Duplicate comments (1)
framework/configstore/migrations_perf_test.go (1)

53-59: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Close the SQLite pool if the migration chain fails.

runPreMergeMigrationChain asserts with require and can stop the goroutine. setupPreMergeSQLiteDB then returns no *gorm.DB, and the caller never registers the cleanup at lines 344-351. The pool stays open. Register the close cleanup directly after gorm.Open succeeds, as the Postgres helper does.

♻️ Proposed fix
 	require.NoError(t, err, "failed to open perf test sqlite db")
 
+	if sqlDB, dbErr := db.DB(); dbErr == nil {
+		t.Cleanup(func() { _ = sqlDB.Close() })
+	}
+
 	runPreMergeMigrationChain(t, db)
 	return db
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@framework/configstore/migrations_perf_test.go` around lines 53 - 59, In
setupPreMergeSQLiteDB, register the SQLite database cleanup immediately after
the successful gorm.Open call and before runPreMergeMigrationChain executes.
Reuse the existing cleanup pattern used by the Postgres helper so the pool
closes even when runPreMergeMigrationChain aborts via require.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@framework/configstore/migrations_perf_test.go`:
- Around line 53-59: In setupPreMergeSQLiteDB, register the SQLite database
cleanup immediately after the successful gorm.Open call and before
runPreMergeMigrationChain executes. Reuse the existing cleanup pattern used by
the Postgres helper so the pool closes even when runPreMergeMigrationChain
aborts via require.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: af85c565-a4eb-4b09-9b49-be1b51b48a4a

📥 Commits

Reviewing files that changed from the base of the PR and between f6e41a6 and 5025fd1.

📒 Files selected for processing (15)
  • framework/configstore/encryption.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • framework/configstore/migrations_perf_test.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_mcp_sessions_test.go
  • framework/configstore/rdb_oauth2_test.go
  • framework/configstore/rdb_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/mcpoauth2.go
  • framework/oauth2/main.go
  • framework/oauth2/sync_test.go
  • tests/scripts/migration-checker/main.go
  • transports/bifrost-http/handlers/mcpsessions.go
  • transports/bifrost-http/lib/config_test.go
🚧 Files skipped from review as they are similar to previous changes (14)
  • framework/oauth2/main.go
  • tests/scripts/migration-checker/main.go
  • framework/configstore/rdb_mcp_sessions_test.go
  • framework/configstore/rdb_test.go
  • framework/configstore/encryption.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/handlers/mcpsessions.go
  • framework/configstore/tables/mcpoauth2.go
  • framework/oauth2/sync_test.go
  • framework/configstore/rdb_oauth2_test.go
  • framework/configstore/store.go
  • framework/configstore/migrations.go
  • framework/configstore/encryption_test.go
  • framework/configstore/rdb.go

@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-28-feat_unify_shared_and_per-user_mcp_oauth_token_storage_into_tablemcpoauthtoken branch from 5025fd1 to 7949fe6 Compare August 6, 2026 21:53
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 05-29-feat_ui_support_for_config_json_per_user_headers_mcp branch from f6e41a6 to 4f06391 Compare August 6, 2026 21:53
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
framework/configstore/migrations_perf_test.go (1)

161-168: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove explicit GORM timestamp assignments.

GORM populates CreatedAt and UpdatedAt for these table models. Remove the manual assignments and remove now where it becomes unused.

  • framework/configstore/migrations_perf_test.go#L161-L168: remove CreatedAt and UpdatedAt from TableMCPClient.
  • framework/configstore/migrations_perf_test.go#L225-L255: remove CreatedAt and UpdatedAt from all seeded shared-token records.
  • framework/configstore/migrations_perf_test.go#L280-L295: remove CreatedAt and UpdatedAt from TableOauthUserToken.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@framework/configstore/migrations_perf_test.go` around lines 161 - 168, Remove
the explicit CreatedAt and UpdatedAt assignments from the TableMCPClient seed at
framework/configstore/migrations_perf_test.go#L161-L168, all seeded shared-token
records at framework/configstore/migrations_perf_test.go#L225-L255, and the
TableOauthUserToken seed at
framework/configstore/migrations_perf_test.go#L280-L295; then remove the
now-unused now variable while preserving the existing seed data.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@framework/configstore/migrations_perf_test.go`:
- Around line 161-168: Remove the explicit CreatedAt and UpdatedAt assignments
from the TableMCPClient seed at
framework/configstore/migrations_perf_test.go#L161-L168, all seeded shared-token
records at framework/configstore/migrations_perf_test.go#L225-L255, and the
TableOauthUserToken seed at
framework/configstore/migrations_perf_test.go#L280-L295; then remove the
now-unused now variable while preserving the existing seed data.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 38786e27-735f-42a0-9963-2f2b866ffd0e

📥 Commits

Reviewing files that changed from the base of the PR and between 4f06391 and 7949fe6.

📒 Files selected for processing (15)
  • framework/configstore/encryption.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • framework/configstore/migrations_perf_test.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_mcp_sessions_test.go
  • framework/configstore/rdb_oauth2_test.go
  • framework/configstore/rdb_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/mcpoauth2.go
  • framework/oauth2/main.go
  • framework/oauth2/sync_test.go
  • tests/scripts/migration-checker/main.go
  • transports/bifrost-http/handlers/mcpsessions.go
  • transports/bifrost-http/lib/config_test.go
🚧 Files skipped from review as they are similar to previous changes (14)
  • framework/configstore/rdb_test.go
  • framework/configstore/rdb_oauth2_test.go
  • tests/scripts/migration-checker/main.go
  • framework/configstore/rdb_mcp_sessions_test.go
  • transports/bifrost-http/handlers/mcpsessions.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • transports/bifrost-http/lib/config_test.go
  • framework/configstore/tables/mcpoauth2.go
  • framework/configstore/encryption.go
  • framework/oauth2/main.go
  • framework/oauth2/sync_test.go
  • framework/configstore/store.go
  • framework/configstore/rdb.go

@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-28-feat_unify_shared_and_per-user_mcp_oauth_token_storage_into_tablemcpoauthtoken branch from 7949fe6 to fb096cd Compare August 8, 2026 08:43

Pratham-Mishra04 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • Aug 8, 8:47 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 8, 9:01 AM UTC: Graphite rebased this pull request as part of a merge.
  • Aug 8, 9:02 AM UTC: @Pratham-Mishra04 merged this pull request with Graphite.

@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from 05-29-feat_ui_support_for_config_json_per_user_headers_mcp to graphite-base/5708 August 8, 2026 08:57
@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from graphite-base/5708 to dev August 8, 2026 08:59
@Pratham-Mishra04
Pratham-Mishra04 dismissed coderabbitai[bot]’s stale review August 8, 2026 08:59

The base branch was changed.

…en perf fixtures so the migration's join is exercised, not just its miss path
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 07-28-feat_unify_shared_and_per-user_mcp_oauth_token_storage_into_tablemcpoauthtoken branch from fb096cd to 93937a5 Compare August 8, 2026 09:00
@Pratham-Mishra04
Pratham-Mishra04 merged commit 85d8cb3 into dev Aug 8, 2026
14 of 15 checks passed
@Pratham-Mishra04
Pratham-Mishra04 deleted the 07-28-feat_unify_shared_and_per-user_mcp_oauth_token_storage_into_tablemcpoauthtoken branch August 8, 2026 09:02
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.

2 participants