Add Manage Accounts command and UI (#272042)

* Add Manage Accounts command and UI
Fix for #233881

* Update src/vs/workbench/contrib/authentication/browser/actions/manageAccountsAction.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Dmitriy Vasyura
2025-10-20 14:37:49 -07:00
committed by GitHub
parent 705c8a2977
commit 04da0048f7
2 changed files with 142 additions and 0 deletions
@@ -0,0 +1,140 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Lazy } from '../../../../../base/common/lazy.js';
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
import { localize, localize2 } from '../../../../../nls.js';
import { Action2 } from '../../../../../platform/actions/common/actions.js';
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';
import { IProductService } from '../../../../../platform/product/common/productService.js';
import { IQuickInputService, IQuickPickItem } from '../../../../../platform/quickinput/common/quickInput.js';
import { ISecretStorageService } from '../../../../../platform/secrets/common/secrets.js';
import { getCurrentAuthenticationSessionInfo } from '../../../../services/authentication/browser/authenticationService.js';
import { IAuthenticationProvider, IAuthenticationService } from '../../../../services/authentication/common/authentication.js';
export class ManageAccountsAction extends Action2 {
constructor() {
super({
id: 'workbench.action.manageAccounts',
title: localize2('manageAccounts', "Manage Accounts"),
category: localize2('accounts', "Accounts"),
f1: true
});
}
public override run(accessor: ServicesAccessor): Promise<void> {
const instantiationService = accessor.get(IInstantiationService);
return instantiationService.createInstance(ManageAccountsActionImpl).run();
}
}
interface AccountQuickPickItem extends IQuickPickItem {
providerId: string;
canUseMcp: boolean;
canSignOut: () => Promise<boolean>;
}
interface AccountActionQuickPickItem extends IQuickPickItem {
action: () => void;
}
class ManageAccountsActionImpl {
private activeSession = new Lazy(() => getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService));
constructor(
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
@ICommandService private readonly commandService: ICommandService,
@ISecretStorageService private readonly secretStorageService: ISecretStorageService,
@IProductService private readonly productService: IProductService,
) { }
public async run() {
const placeHolder = localize('pickAccount', "Select an account to manage");
const accounts = await this.listAccounts();
if (!accounts.length) {
await this.quickInputService.pick([{ label: localize('noActiveAccounts', "There are no active accounts.") }], { placeHolder });
return;
}
const account = await this.quickInputService.pick(accounts, { placeHolder, matchOnDescription: true });
if (!account) {
return;
}
await this.showAccountActions(account);
}
private async listAccounts(): Promise<AccountQuickPickItem[]> {
const accounts: AccountQuickPickItem[] = [];
for (const providerId of this.authenticationService.getProviderIds()) {
const provider = this.authenticationService.getProvider(providerId);
for (const { label, id } of await this.authenticationService.getAccounts(providerId)) {
accounts.push({
label,
description: provider.label,
providerId,
canUseMcp: !!provider.authorizationServers?.length,
canSignOut: () => this.canSignOut(provider, id)
});
}
}
return accounts;
}
private async canSignOut(provider: IAuthenticationProvider, accountId: string): Promise<boolean> {
const session = await this.activeSession.value;
if (session && !session.canSignOut && session.providerId === provider.id) {
const sessions = await this.authenticationService.getSessions(provider.id);
return !sessions.some(o => o.id === session.id && o.account.id === accountId);
}
return true;
}
private async showAccountActions(account: AccountQuickPickItem): Promise<void> {
const { providerId, label: accountLabel, canUseMcp, canSignOut } = account;
const store = new DisposableStore();
const quickPick = store.add(this.quickInputService.createQuickPick<AccountActionQuickPickItem>());
quickPick.title = localize('manageAccount', "Manage '{0}'", accountLabel);
quickPick.placeholder = localize('selectAction', "Select an action");
const items: AccountActionQuickPickItem[] = [{
label: localize('manageTrustedExtensions', "Manage Trusted Extensions"),
action: () => this.commandService.executeCommand('_manageTrustedExtensionsForAccount', { providerId, accountLabel })
}];
if (canUseMcp) {
items.push({
label: localize('manageTrustedMCPServers', "Manage Trusted MCP Servers"),
action: () => this.commandService.executeCommand('_manageTrustedMCPServersForAccount', { providerId, accountLabel })
});
}
if (await canSignOut()) {
items.push({
label: localize('signOut', "Sign Out"),
action: () => this.commandService.executeCommand('_signOutOfAccount', { providerId, accountLabel })
});
}
quickPick.items = items;
store.add(quickPick.onDidAccept(() => {
const selected = quickPick.selectedItems[0];
if (selected) {
quickPick.hide();
selected.action();
}
}));
store.add(quickPick.onDidHide(() => store.dispose()));
quickPick.show();
}
}
@@ -20,6 +20,7 @@ import { IAuthenticationUsageService } from '../../../services/authentication/br
import { ManageAccountPreferencesForMcpServerAction } from './actions/manageAccountPreferencesForMcpServerAction.js';
import { ManageTrustedMcpServersForAccountAction } from './actions/manageTrustedMcpServersForAccountAction.js';
import { RemoveDynamicAuthenticationProvidersAction } from './actions/manageDynamicAuthenticationProvidersAction.js';
import { ManageAccountsAction } from './actions/manageAccountsAction.js';
import { IAuthenticationQueryService } from '../../../services/authentication/common/authenticationQuery.js';
import { IMcpRegistry } from '../../mcp/common/mcpRegistryTypes.js';
import { autorun } from '../../../../base/common/observable.js';
@@ -92,6 +93,7 @@ class AuthenticationContribution extends Disposable implements IWorkbenchContrib
}
private _registerActions(): void {
this._register(registerAction2(ManageAccountsAction));
this._register(registerAction2(SignOutOfAccountAction));
this._register(registerAction2(ManageTrustedExtensionsForAccountAction));
this._register(registerAction2(ManageAccountPreferencesForExtensionAction));