Skip to content

Commit 061f496

Browse files
author
Rachel Macfarlane
committed
Show sign in entry for all auth providers in accounts menu, fixes microsoft#94488
1 parent 59e4b45 commit 061f496

7 files changed

Lines changed: 61 additions & 60 deletions

File tree

extensions/github-authentication/src/extension.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ export async function activate(context: vscode.ExtensionContext) {
1818
vscode.authentication.registerAuthenticationProvider({
1919
id: 'github',
2020
displayName: 'GitHub',
21+
supportsMultipleAccounts: false,
2122
onDidChangeSessions: onDidChangeSessions.event,
2223
getSessions: () => Promise.resolve(loginService.sessions),
23-
login: async (scopeList: string[]) => {
24+
login: async (scopeList: string[] | undefined) => {
2425
try {
25-
const session = await loginService.login(scopeList.join(' '));
26+
const loginScopes = scopeList ? scopeList.sort().join(' ') : 'user:email';
27+
const session = await loginService.login(loginScopes);
2628
Logger.info('Login success!');
2729
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
2830
return session;

extensions/vscode-account/src/extension.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,15 @@ export async function activate(context: vscode.ExtensionContext) {
2020
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({
2121
id: 'microsoft',
2222
displayName: 'Microsoft',
23+
supportsMultipleAccounts: true,
2324
onDidChangeSessions: onDidChangeSessions.event,
2425
getSessions: () => Promise.resolve(loginService.sessions),
25-
login: async (scopes: string[]) => {
26-
try {
27-
await loginService.login(scopes.sort().join(' '));
28-
const session = loginService.sessions[loginService.sessions.length - 1];
29-
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
30-
return loginService.sessions[0]!;
31-
} catch (e) {
32-
throw e;
33-
}
26+
login: async (scopes: string[] | undefined) => {
27+
const loginScopes = scopes ? scopes.sort().join(' ') : 'https://management.core.windows.net/.default offline_access';
28+
await loginService.login(loginScopes);
29+
const session = loginService.sessions[loginService.sessions.length - 1];
30+
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
31+
return loginService.sessions[0]!;
3432
},
3533
logout: async (id: string) => {
3634
await loginService.logout(id);

src/vs/vscode.proposed.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ declare module 'vscode' {
7474
readonly id: string;
7575
readonly displayName: string;
7676

77+
/**
78+
* Whether the authentication provider supports the user being logged into
79+
* multiple different accounts at the same time.
80+
*/
81+
supportsMultipleAccounts: boolean;
82+
7783
/**
7884
* An [event](#Event) which fires when the array of sessions has changed, or data
7985
* within a session has changed.
@@ -88,7 +94,7 @@ declare module 'vscode' {
8894
/**
8995
* Prompts a user to login.
9096
*/
91-
login(scopes: string[]): Thenable<AuthenticationSession>;
97+
login(scopes?: string[]): Thenable<AuthenticationSession>;
9298
logout(sessionId: string): Thenable<void>;
9399
}
94100

src/vs/workbench/api/browser/mainThreadAuthentication.ts

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
1717
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
1818
import { INotificationService } from 'vs/platform/notification/common/notification';
1919

20-
interface AuthDependent {
21-
providerId: string;
22-
label: string;
23-
scopes: string[];
24-
scopeDescriptions?: string;
25-
}
26-
27-
const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [
28-
{
29-
providerId: 'microsoft',
30-
label: 'Settings sync',
31-
scopes: ['https://management.core.windows.net/.default', 'offline_access'],
32-
scopeDescriptions: 'Read user email'
33-
}
34-
];
35-
3620
interface AllowedExtension {
3721
id: string;
3822
name: string;
@@ -74,12 +58,13 @@ export class MainThreadAuthenticationProvider extends Disposable {
7458
private _accounts = new Map<string, string[]>(); // Map account name to session ids
7559
private _sessions = new Map<string, string>(); // Map account id to name
7660
private _signInMenuItem: IMenuItem | undefined;
61+
private _signInMenuDisposables: IDisposable[] = [];
7762

7863
constructor(
7964
private readonly _proxy: ExtHostAuthenticationShape,
8065
public readonly id: string,
8166
public readonly displayName: string,
82-
public readonly dependents: AuthDependent[],
67+
private readonly supportsMultipleAccounts: boolean,
8368
private readonly notificationService: INotificationService
8469
) {
8570
super();
@@ -135,29 +120,33 @@ export class MainThreadAuthenticationProvider extends Disposable {
135120
quickPick.show();
136121
}
137122

138-
private async registerCommandsAndContextMenuItems(): Promise<void> {
139-
const sessions = await this._proxy.$getSessions(this.id);
123+
private createSignInMenu(hasSessions: boolean): void {
124+
this._signInMenuDisposables.push(CommandsRegistry.registerCommand({
125+
id: `signIn${this.id}`,
126+
handler: (accessor, args) => {
127+
this.login();
128+
},
129+
}));
140130

141-
if (this.dependents.length) {
142-
this._register(CommandsRegistry.registerCommand({
131+
this._signInMenuItem = {
132+
group: '2_providers',
133+
command: {
143134
id: `signIn${this.id}`,
144-
handler: (accessor, args) => {
145-
this.login(this.dependents.reduce((previous: string[], current) => previous.concat(current.scopes), []));
146-
},
147-
}));
148-
149-
this._signInMenuItem = {
150-
group: '2_providers',
151-
command: {
152-
id: `signIn${this.id}`,
153-
title: sessions.length
154-
? nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName)
155-
: nls.localize('addAccount', "Sign in to {0}", this.displayName)
156-
},
157-
order: 3
158-
};
135+
title: hasSessions
136+
? nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName)
137+
: nls.localize('addAccount', "Sign in to {0}", this.displayName)
138+
},
139+
order: 3
140+
};
159141

160-
this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, this._signInMenuItem));
142+
this._signInMenuDisposables.push(MenuRegistry.appendMenuItem(MenuId.AccountsContext, this._signInMenuItem));
143+
}
144+
145+
private async registerCommandsAndContextMenuItems(): Promise<void> {
146+
const sessions = await this._proxy.$getSessions(this.id);
147+
148+
if (!sessions.length || (sessions.length && this.supportsMultipleAccounts)) {
149+
this.createSignInMenu(!!sessions.length);
161150
}
162151

163152
sessions.forEach(session => this.registerSession(session));
@@ -261,6 +250,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
261250

262251
if (this._signInMenuItem) {
263252
this._signInMenuItem.command.title = nls.localize('addAccount', "Sign in to {0}", this.displayName);
253+
} else {
254+
this.createSignInMenu(false);
264255
}
265256
}
266257
}
@@ -269,11 +260,16 @@ export class MainThreadAuthenticationProvider extends Disposable {
269260
addedSessions.forEach(session => this.registerSession(session));
270261

271262
if (addedSessions.length && this._signInMenuItem) {
272-
this._signInMenuItem.command.title = nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName);
263+
if (this.supportsMultipleAccounts) {
264+
this._signInMenuItem.command.title = nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName);
265+
} else {
266+
this._signInMenuDisposables.forEach(item => item.dispose());
267+
this._signInMenuItem = undefined;
268+
}
273269
}
274270
}
275271

276-
login(scopes: string[]): Promise<modes.AuthenticationSession> {
272+
login(scopes?: string[]): Promise<modes.AuthenticationSession> {
277273
return this._proxy.$login(this.id, scopes).then(session => {
278274
return {
279275
id: session.id,
@@ -292,6 +288,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
292288
super.dispose();
293289
this._sessionMenuItems.forEach(item => item.forEach(d => d.dispose()));
294290
this._sessionMenuItems.clear();
291+
this._signInMenuDisposables.forEach(item => item.dispose());
295292
}
296293
}
297294

@@ -310,10 +307,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
310307
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
311308
}
312309

313-
async $registerAuthenticationProvider(id: string, displayName: string): Promise<void> {
314-
const dependentBuiltIns = BUILT_IN_AUTH_DEPENDENTS.filter(dependency => dependency.providerId === id);
315-
316-
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, dependentBuiltIns, this.notificationService);
310+
async $registerAuthenticationProvider(id: string, displayName: string, supportsMultipleAccounts: boolean): Promise<void> {
311+
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, supportsMultipleAccounts, this.notificationService);
317312
this.authenticationService.registerAuthenticationProvider(id, provider);
318313
}
319314

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export interface MainThreadCommentsShape extends IDisposable {
155155
}
156156

157157
export interface MainThreadAuthenticationShape extends IDisposable {
158-
$registerAuthenticationProvider(id: string, displayName: string): void;
158+
$registerAuthenticationProvider(id: string, displayName: string, supportsMultipleAccounts: boolean): void;
159159
$unregisterAuthenticationProvider(id: string): void;
160160
$onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
161161
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
@@ -997,7 +997,7 @@ export interface ExtHostLabelServiceShape {
997997
export interface ExtHostAuthenticationShape {
998998
$getSessions(id: string): Promise<ReadonlyArray<modes.AuthenticationSession>>;
999999
$getSessionAccessToken(id: string, sessionId: string): Promise<string>;
1000-
$login(id: string, scopes: string[]): Promise<modes.AuthenticationSession>;
1000+
$login(id: string, scopes: string[] | undefined): Promise<modes.AuthenticationSession>;
10011001
$logout(id: string, sessionId: string): Promise<void>;
10021002
}
10031003

src/vs/workbench/api/common/extHostAuthentication.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
114114
this._onDidChangeSessions.fire({ [provider.id]: e });
115115
});
116116

117-
this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName);
117+
this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName, provider.supportsMultipleAccounts);
118118
this._onDidChangeAuthenticationProviders.fire({ added: [provider.id], removed: [] });
119119

120120
return new Disposable(() => {
@@ -125,7 +125,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
125125
});
126126
}
127127

128-
$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession> {
128+
$login(providerId: string, scopes: string[] | undefined): Promise<modes.AuthenticationSession> {
129129
const authProvider = this._authenticationProviders.get(providerId);
130130
if (authProvider) {
131131
return Promise.resolve(authProvider.login(scopes));

src/vs/workbench/services/authentication/browser/authenticationService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class AuthenticationService extends Disposable implements IAuthentication
6060
this._authenticationProviders.set(id, authenticationProvider);
6161
this._onDidRegisterAuthenticationProvider.fire(id);
6262

63-
if (authenticationProvider.dependents.length && this._placeholderMenuItem) {
63+
if (this._placeholderMenuItem) {
6464
this._placeholderMenuItem.dispose();
6565
this._placeholderMenuItem = undefined;
6666
}

0 commit comments

Comments
 (0)