mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add UI option to disconnect OAuth2 MCP credentials (#27299)
Closes [CODAGT-804](https://linear.app/codercom/issue/CODAGT-804/add-ui-option-to-revoke-oauth-mcp-credentials). Users could authenticate with an OAuth2 MCP server from the chat input, but there was no UI to disconnect those per-user credentials. The backend endpoint (`DELETE /api/experimental/mcp/servers/{id}/oauth2/disconnect`) already existed. ## Changes - Connected OAuth2 MCP rows in the chat input plus menu now show a disconnect icon button next to the enable switch. It opens a confirmation dialog; confirming calls the disconnect endpoint, shows a toast, and refetches MCP configs so the row reverts to the `Auth` button without a reload. - New `disconnectMCPServerOAuth2` API client method and react-query mutation that invalidates `mcp-server-configs`. - Storybook interaction tests: control visibility per auth state, cancel makes no API call, confirm calls the endpoint once, failed disconnect keeps the dialog open. - Hardened `TestMCPServerConfigsOAuth2Disconnect`: seeded tokens flip `auth_connected`, disconnect only removes the calling user's token, and repeat disconnect stays idempotent. The endpoint removes the token stored in Coder; it does not revoke the upstream OAuth grant, so the UI copy says "disconnect" rather than "revoke". Validated with the targeted Go test, Storybook tests (51 passed), tsc, biome, the react-compiler check, and a manual dogfood run (seeded token, disconnect/cancel/reconnect flows verified in the UI). > This PR was authored by Mux, an AI coding agent, on Mike's behalf.
This commit is contained in:
+37
-2
@@ -526,9 +526,14 @@ func TestMCPServerConfigsOAuth2Disconnect(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
adminClient := newMCPClient(t)
|
||||
providerKeys := coderdtest.FakeOpenAICompatProviderAPIKeys(t)
|
||||
adminClient, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
DeploymentValues: mcpDeploymentValues(t),
|
||||
ChatProviderAPIKeys: &providerKeys,
|
||||
})
|
||||
firstUser := coderdtest.CreateFirstUser(t, adminClient)
|
||||
memberClient, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID)
|
||||
memberClient, member := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID)
|
||||
otherClient, other := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID)
|
||||
|
||||
created, err := adminClient.CreateMCPServerConfig(ctx, codersdk.CreateMCPServerConfigRequest{
|
||||
DisplayName: "OAuth Disconnect Test",
|
||||
@@ -549,6 +554,36 @@ func TestMCPServerConfigsOAuth2Disconnect(t *testing.T) {
|
||||
// Disconnect should succeed even when no token exists (idempotent).
|
||||
err = memberClient.MCPServerOAuth2Disconnect(ctx, created.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, userID := range []uuid.UUID{member.ID, other.ID} {
|
||||
//nolint:gocritic // Seeding test state requires system access.
|
||||
_, err = db.UpsertMCPServerUserToken(dbauthz.AsSystemRestricted(ctx), database.UpsertMCPServerUserTokenParams{
|
||||
MCPServerConfigID: created.ID,
|
||||
UserID: userID,
|
||||
AccessToken: "valid-access",
|
||||
TokenType: "Bearer",
|
||||
Expiry: sql.NullTime{Time: time.Now().Add(time.Hour), Valid: true},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
requireAuthConnected := func(client *codersdk.Client, want bool) {
|
||||
t.Helper()
|
||||
configs, err := client.MCPServerConfigs(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, configs, 1)
|
||||
require.Equal(t, want, configs[0].AuthConnected)
|
||||
}
|
||||
requireAuthConnected(memberClient, true)
|
||||
requireAuthConnected(otherClient, true)
|
||||
|
||||
err = memberClient.MCPServerOAuth2Disconnect(ctx, created.ID)
|
||||
require.NoError(t, err)
|
||||
requireAuthConnected(memberClient, false)
|
||||
requireAuthConnected(otherClient, true)
|
||||
|
||||
err = memberClient.MCPServerOAuth2Disconnect(ctx, created.ID)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestMCPServerConfigsOAuth2AutoDiscovery(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user