Show sign in entry for all auth providers in accounts menu, fixes #94488

This commit is contained in:
Rachel Macfarlane
2020-04-13 14:08:22 -07:00
parent 59e4b4562f
commit 061f4967f9
7 changed files with 61 additions and 60 deletions

View File

@@ -18,11 +18,13 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.authentication.registerAuthenticationProvider({
id: 'github',
displayName: 'GitHub',
supportsMultipleAccounts: false,
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),
login: async (scopeList: string[]) => {
login: async (scopeList: string[] | undefined) => {
try {
const session = await loginService.login(scopeList.join(' '));
const loginScopes = scopeList ? scopeList.sort().join(' ') : 'user:email';
const session = await loginService.login(loginScopes);
Logger.info('Login success!');
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
return session;

View File

@@ -20,17 +20,15 @@ export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.authentication.registerAuthenticationProvider({
id: 'microsoft',
displayName: 'Microsoft',
supportsMultipleAccounts: true,
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),
login: async (scopes: string[]) => {
try {
await loginService.login(scopes.sort().join(' '));
const session = loginService.sessions[loginService.sessions.length - 1];
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
return loginService.sessions[0]!;
} catch (e) {
throw e;
}
login: async (scopes: string[] | undefined) => {
const loginScopes = scopes ? scopes.sort().join(' ') : 'https://management.core.windows.net/.default offline_access';
await loginService.login(loginScopes);
const session = loginService.sessions[loginService.sessions.length - 1];
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
return loginService.sessions[0]!;
},
logout: async (id: string) => {
await loginService.logout(id);

View File

@@ -74,6 +74,12 @@ declare module 'vscode' {
readonly id: string;
readonly displayName: string;
/**
* Whether the authentication provider supports the user being logged into
* multiple different accounts at the same time.
*/
supportsMultipleAccounts: boolean;
/**
* An [event](#Event) which fires when the array of sessions has changed, or data
* within a session has changed.
@@ -88,7 +94,7 @@ declare module 'vscode' {
/**
* Prompts a user to login.
*/
login(scopes: string[]): Thenable<AuthenticationSession>;
login(scopes?: string[]): Thenable<AuthenticationSession>;
logout(sessionId: string): Thenable<void>;
}

View File

@@ -17,22 +17,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService } from 'vs/platform/notification/common/notification';
interface AuthDependent {
providerId: string;
label: string;
scopes: string[];
scopeDescriptions?: string;
}
const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [
{
providerId: 'microsoft',
label: 'Settings sync',
scopes: ['https://management.core.windows.net/.default', 'offline_access'],
scopeDescriptions: 'Read user email'
}
];
interface AllowedExtension {
id: string;
name: string;
@@ -74,12 +58,13 @@ export class MainThreadAuthenticationProvider extends Disposable {
private _accounts = new Map<string, string[]>(); // Map account name to session ids
private _sessions = new Map<string, string>(); // Map account id to name
private _signInMenuItem: IMenuItem | undefined;
private _signInMenuDisposables: IDisposable[] = [];
constructor(
private readonly _proxy: ExtHostAuthenticationShape,
public readonly id: string,
public readonly displayName: string,
public readonly dependents: AuthDependent[],
private readonly supportsMultipleAccounts: boolean,
private readonly notificationService: INotificationService
) {
super();
@@ -135,29 +120,33 @@ export class MainThreadAuthenticationProvider extends Disposable {
quickPick.show();
}
private createSignInMenu(hasSessions: boolean): void {
this._signInMenuDisposables.push(CommandsRegistry.registerCommand({
id: `signIn${this.id}`,
handler: (accessor, args) => {
this.login();
},
}));
this._signInMenuItem = {
group: '2_providers',
command: {
id: `signIn${this.id}`,
title: hasSessions
? nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName)
: nls.localize('addAccount', "Sign in to {0}", this.displayName)
},
order: 3
};
this._signInMenuDisposables.push(MenuRegistry.appendMenuItem(MenuId.AccountsContext, this._signInMenuItem));
}
private async registerCommandsAndContextMenuItems(): Promise<void> {
const sessions = await this._proxy.$getSessions(this.id);
if (this.dependents.length) {
this._register(CommandsRegistry.registerCommand({
id: `signIn${this.id}`,
handler: (accessor, args) => {
this.login(this.dependents.reduce((previous: string[], current) => previous.concat(current.scopes), []));
},
}));
this._signInMenuItem = {
group: '2_providers',
command: {
id: `signIn${this.id}`,
title: sessions.length
? nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName)
: nls.localize('addAccount', "Sign in to {0}", this.displayName)
},
order: 3
};
this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, this._signInMenuItem));
if (!sessions.length || (sessions.length && this.supportsMultipleAccounts)) {
this.createSignInMenu(!!sessions.length);
}
sessions.forEach(session => this.registerSession(session));
@@ -261,6 +250,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
if (this._signInMenuItem) {
this._signInMenuItem.command.title = nls.localize('addAccount', "Sign in to {0}", this.displayName);
} else {
this.createSignInMenu(false);
}
}
}
@@ -269,11 +260,16 @@ export class MainThreadAuthenticationProvider extends Disposable {
addedSessions.forEach(session => this.registerSession(session));
if (addedSessions.length && this._signInMenuItem) {
this._signInMenuItem.command.title = nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName);
if (this.supportsMultipleAccounts) {
this._signInMenuItem.command.title = nls.localize('addAnotherAccount', "Sign in to another {0} account", this.displayName);
} else {
this._signInMenuDisposables.forEach(item => item.dispose());
this._signInMenuItem = undefined;
}
}
}
login(scopes: string[]): Promise<modes.AuthenticationSession> {
login(scopes?: string[]): Promise<modes.AuthenticationSession> {
return this._proxy.$login(this.id, scopes).then(session => {
return {
id: session.id,
@@ -292,6 +288,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
super.dispose();
this._sessionMenuItems.forEach(item => item.forEach(d => d.dispose()));
this._sessionMenuItems.clear();
this._signInMenuDisposables.forEach(item => item.dispose());
}
}
@@ -310,10 +307,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
}
async $registerAuthenticationProvider(id: string, displayName: string): Promise<void> {
const dependentBuiltIns = BUILT_IN_AUTH_DEPENDENTS.filter(dependency => dependency.providerId === id);
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, dependentBuiltIns, this.notificationService);
async $registerAuthenticationProvider(id: string, displayName: string, supportsMultipleAccounts: boolean): Promise<void> {
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, supportsMultipleAccounts, this.notificationService);
this.authenticationService.registerAuthenticationProvider(id, provider);
}

View File

@@ -155,7 +155,7 @@ export interface MainThreadCommentsShape extends IDisposable {
}
export interface MainThreadAuthenticationShape extends IDisposable {
$registerAuthenticationProvider(id: string, displayName: string): void;
$registerAuthenticationProvider(id: string, displayName: string, supportsMultipleAccounts: boolean): void;
$unregisterAuthenticationProvider(id: string): void;
$onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
@@ -997,7 +997,7 @@ export interface ExtHostLabelServiceShape {
export interface ExtHostAuthenticationShape {
$getSessions(id: string): Promise<ReadonlyArray<modes.AuthenticationSession>>;
$getSessionAccessToken(id: string, sessionId: string): Promise<string>;
$login(id: string, scopes: string[]): Promise<modes.AuthenticationSession>;
$login(id: string, scopes: string[] | undefined): Promise<modes.AuthenticationSession>;
$logout(id: string, sessionId: string): Promise<void>;
}

View File

@@ -114,7 +114,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
this._onDidChangeSessions.fire({ [provider.id]: e });
});
this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName);
this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName, provider.supportsMultipleAccounts);
this._onDidChangeAuthenticationProviders.fire({ added: [provider.id], removed: [] });
return new Disposable(() => {
@@ -125,7 +125,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
});
}
$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession> {
$login(providerId: string, scopes: string[] | undefined): Promise<modes.AuthenticationSession> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return Promise.resolve(authProvider.login(scopes));

View File

@@ -60,7 +60,7 @@ export class AuthenticationService extends Disposable implements IAuthentication
this._authenticationProviders.set(id, authenticationProvider);
this._onDidRegisterAuthenticationProvider.fire(id);
if (authenticationProvider.dependents.length && this._placeholderMenuItem) {
if (this._placeholderMenuItem) {
this._placeholderMenuItem.dispose();
this._placeholderMenuItem = undefined;
}