Skip to content

Add command to sign out of multiple accounts at once #288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,11 @@
"command": "intersystems-community.servermanager.viewWebApp",
"title": "View Files in Web Application",
"icon": "$(eye)"
},
{
"command": "intersystems-community.servermanager.signOut",
"category": "InterSystems Server Manager",
"title": "Sign Out of Accounts..."
}
],
"submenus": [
Expand Down Expand Up @@ -541,6 +546,11 @@
"when": "view == intersystems-community_servermanager",
"group": "1_edit"
},
{
"command": "intersystems-community.servermanager.signOut",
"when": "view == intersystems-community_servermanager",
"group": "1_edit"
},
{
"command": "intersystems-community.servermanager.importServers",
"when": "view == intersystems-community_servermanager && isWindows",
Expand Down
41 changes: 39 additions & 2 deletions src/authenticationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
const enteredPassword = inputBox.value;
if (secretStorage && enteredPassword) {
await secretStorage.store(credentialKey, enteredPassword);
console.log(`Stored password at ${credentialKey}`);
}
// Resolve the promise and tidy up
resolve(enteredPassword);
Expand Down Expand Up @@ -270,7 +269,6 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
if (deletePassword) {
// Delete from secret storage
await this.secretStorage.delete(credentialKey);
console.log(`${AUTHENTICATION_PROVIDER_LABEL}: Deleted password at ${credentialKey}`);
}
if (index > -1) {
// Remove session here so we don't store it
Expand All @@ -280,6 +278,45 @@ export class ServerManagerAuthenticationProvider implements AuthenticationProvid
this._onDidChangeSessions.fire({ added: [], removed: [session], changed: [] });
}

public async removeSessions(sessionIds: string[]): Promise<void> {
const storedPasswordCredKeys: string[] = [];
const removed: AuthenticationSession[] = [];
await Promise.allSettled(sessionIds.map(async (sessionId) => {
const index = this._sessions.findIndex((item) => item.id === sessionId);
const session = this._sessions[index];
const credentialKey = ServerManagerAuthenticationProvider.credentialKey(sessionId);
if (await this.secretStorage.get(credentialKey) !== undefined) {
storedPasswordCredKeys.push(credentialKey);
}
if (index > -1) {
this._sessions.splice(index, 1);
}
removed.push(session);
}));
if (storedPasswordCredKeys.length) {
const passwordOption = workspace.getConfiguration("intersystemsServerManager.credentialsProvider")
.get<string>("deletePasswordOnSignout", "ask");
let deletePasswords = (passwordOption === "always");
if (passwordOption === "ask") {
const choice = await window.showWarningMessage(
`Do you want to keep the stored passwords or delete them?`,
{
detail: `${storedPasswordCredKeys.length == sessionIds.length ? "All" : "Some"
} of the ${AUTHENTICATION_PROVIDER_LABEL} accounts you signed out are currently storing their passwords securely on your workstation.`, modal: true
},
{ title: "Keep", isCloseAffordance: true },
{ title: "Delete", isCloseAffordance: false },
);
deletePasswords = (choice?.title === "Delete");
}
if (deletePasswords) {
await Promise.allSettled(storedPasswordCredKeys.map((e) => this.secretStorage.delete(e)));
}
}
await this._storeStrippedSessions();
this._onDidChangeSessions.fire({ added: [], removed, changed: [] });
}

private async _ensureInitialized(): Promise<void> {
if (this._initializedDisposable === undefined) {

Expand Down
15 changes: 13 additions & 2 deletions src/commonActivate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,14 @@ export function commonActivate(context: vscode.ExtensionContext, view: ServerMan
}
};

const authProvider = new ServerManagerAuthenticationProvider(context.secrets);
context.subscriptions.push(
// Register our authentication provider. NOTE: this will register the provider globally which means that
// any other extension can request to use use this provider via the `vscode.authentication.getSession` API.
vscode.authentication.registerAuthenticationProvider(
ServerManagerAuthenticationProvider.id,
ServerManagerAuthenticationProvider.label,
new ServerManagerAuthenticationProvider(context.secrets),
authProvider,
{ supportsMultipleAccounts: true },
),
// Ensure cookies do not survive an account sign-out
Expand Down Expand Up @@ -136,7 +137,7 @@ export function commonActivate(context: vscode.ExtensionContext, view: ServerMan
}
const choice = await vscode.window.showQuickPick(options, {
ignoreFocusOut: true,
title: "Pick a settngs scope in which to add the server definition"
title: "Pick a settings scope in which to add the server definition"
});
if (!choice) return;
scope = choice.scope;
Expand Down Expand Up @@ -330,6 +331,16 @@ export function commonActivate(context: vscode.ExtensionContext, view: ServerMan
await addWorkspaceFolderAsync(true, true, <NamespaceTreeItem>webAppTreeItem?.parent?.parent, undefined, webAppTreeItem?.name);
}),
vscode.workspace.onDidChangeWorkspaceFolders(() => view.refreshTree()),
vscode.commands.registerCommand(`${extensionId}.signOut`, async () => {
const sessions = await authProvider.getSessions(undefined, {}).catch(() => { });
if (!sessions?.length) {
vscode.window.showInformationMessage("There are no stored accounts to sign out of.", "Dismiss");
return;
}
const picks = await vscode.window.showQuickPick(sessions.map((s) => s.account), { canPickMany: true, title: "Pick the accounts to sign out of" });
if (!picks?.length) return;
return authProvider.removeSessions(picks.map((p) => p.id));
}),
// Listen for relevant configuration changes
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration("intersystems.servers") || e.affectsConfiguration("objectscript.conn")) {
Expand Down