Skip to content

Commit

Permalink
Proposed API for forceNewSession taking in a session to re-create
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerLeonhardt committed May 9, 2023
1 parent d1aa00a commit 09bf7d9
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 21 deletions.
23 changes: 17 additions & 6 deletions src/vs/workbench/api/browser/mainThreadAuthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent, addAccountUsage, readAccountUsages, removeAccountUsage } from 'vs/workbench/services/authentication/browser/authenticationService';
import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IAuthenticationCreateSessionOptions, AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { ExtHostAuthenticationShape, ExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
Expand Down Expand Up @@ -119,8 +119,8 @@ export class MainThreadAuthenticationProvider extends Disposable implements IAut
return this._proxy.$getSessions(this.id, scopes);
}

createSession(scopes: string[]): Promise<AuthenticationSession> {
return this._proxy.$createSession(this.id, scopes);
createSession(scopes: string[], options: IAuthenticationCreateSessionOptions): Promise<AuthenticationSession> {
return this._proxy.$createSession(this.id, scopes, options);
}

async removeSession(sessionId: string): Promise<void> {
Expand Down Expand Up @@ -242,9 +242,20 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
throw new Error('User did not consent to login.');
}

const session = sessions?.length && !options.forceNewSession && supportsMultipleAccounts
? await this.authenticationService.selectSession(providerId, extensionId, extensionName, scopes, sessions)
: await this.authenticationService.createSession(providerId, scopes, true);
let session;
if (sessions?.length && !options.forceNewSession && supportsMultipleAccounts) {
session = await this.authenticationService.selectSession(providerId, extensionId, extensionName, scopes, sessions);
} else {
let sessionToRecreate: AuthenticationSession | undefined;
if (typeof options.forceNewSession === 'object' && options.forceNewSession.sessionToRecreate) {
sessionToRecreate = options.forceNewSession.sessionToRecreate as AuthenticationSession;
} else {
const sessionIdToRecreate = this.authenticationService.getSessionPreference(providerId, extensionId, scopes);
sessionToRecreate = sessionIdToRecreate ? sessions.find(session => session.id === sessionIdToRecreate) : undefined;
}
session = await this.authenticationService.createSession(providerId, scopes, { activateImmediate: true, sessionToRecreate });
}

this.authenticationService.updateAllowedExtension(providerId, session.account.label, extensionId, extensionName, true);
this.authenticationService.updateSessionPreference(providerId, extensionId, session);
return session;
Expand Down
11 changes: 8 additions & 3 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import { IWorkspaceSymbol } from 'vs/workbench/contrib/search/common/search';
import { CoverageDetails, ExtensionRunTestsRequest, ICallProfileRunHandler, IFileCoverage, ISerializedTestResults, IStartControllerTests, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, TestResultState, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes';
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
import { TypeHierarchyItem } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
import { AuthenticationProviderInformation, AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/workbench/services/authentication/common/authentication';
import { IAuthenticationCreateSessionOptions, AuthenticationProviderInformation, AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/workbench/services/authentication/common/authentication';
import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
import { IExtensionDescriptionDelta, IStaticWorkspaceData } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
import { IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy';
Expand Down Expand Up @@ -144,12 +144,17 @@ export interface MainThreadCommentsShape extends IDisposable {
$updateCommentingRanges(handle: number): void;
}

export interface AuthenticationForceNewSessionOptions {
detail?: string;
sessionToRecreate?: AuthenticationSession;
}

export interface MainThreadAuthenticationShape extends IDisposable {
$registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): void;
$unregisterAuthenticationProvider(id: string): void;
$ensureProvider(id: string): Promise<void>;
$sendDidChangeSessions(providerId: string, event: AuthenticationSessionsChangeEvent): void;
$getSession(providerId: string, scopes: readonly string[], extensionId: string, extensionName: string, options: { createIfNone?: boolean; forceNewSession?: boolean | { detail: string }; clearSessionPreference?: boolean }): Promise<AuthenticationSession | undefined>;
$getSession(providerId: string, scopes: readonly string[], extensionId: string, extensionName: string, options: { createIfNone?: boolean; forceNewSession?: boolean | AuthenticationForceNewSessionOptions; clearSessionPreference?: boolean }): Promise<AuthenticationSession | undefined>;
$getSessions(providerId: string, scopes: readonly string[], extensionId: string, extensionName: string): Promise<AuthenticationSession[]>;
$removeSession(providerId: string, sessionId: string): Promise<void>;
}
Expand Down Expand Up @@ -1558,7 +1563,7 @@ export interface ExtHostLabelServiceShape {

export interface ExtHostAuthenticationShape {
$getSessions(id: string, scopes?: string[]): Promise<ReadonlyArray<AuthenticationSession>>;
$createSession(id: string, scopes: string[]): Promise<AuthenticationSession>;
$createSession(id: string, scopes: string[], options: IAuthenticationCreateSessionOptions): Promise<AuthenticationSession>;
$removeSession(id: string, sessionId: string): Promise<void>;
$onDidChangeAuthenticationSessions(id: string, label: string): Promise<void>;
$setProviders(providers: AuthenticationProviderInformation[]): Promise<void>;
Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/api/common/extHostAuthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
return Promise.resolve();
}

async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[], options: vscode.AuthenticationGetSessionOptions & ({ createIfNone: true } | { forceNewSession: true } | { forceNewSession: { detail: string } })): Promise<vscode.AuthenticationSession>;
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[], options: vscode.AuthenticationGetSessionOptions & ({ createIfNone: true } | { forceNewSession: true } | { forceNewSession: vscode.AuthenticationForceNewSessionOptions })): Promise<vscode.AuthenticationSession>;
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[], options: vscode.AuthenticationGetSessionOptions & { forceNewSession: true }): Promise<vscode.AuthenticationSession>;
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[], options: vscode.AuthenticationGetSessionOptions & { forceNewSession: { detail: string } }): Promise<vscode.AuthenticationSession>;
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[], options: vscode.AuthenticationGetSessionOptions & { forceNewSession: vscode.AuthenticationForceNewSessionOptions }): Promise<vscode.AuthenticationSession>;
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[], options: vscode.AuthenticationGetSessionOptions): Promise<vscode.AuthenticationSession | undefined>;
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[], options: vscode.AuthenticationGetSessionOptions = {}): Promise<vscode.AuthenticationSession | undefined> {
const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier);
Expand Down Expand Up @@ -106,10 +106,10 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
});
}

$createSession(providerId: string, scopes: string[]): Promise<vscode.AuthenticationSession> {
$createSession(providerId: string, scopes: string[], options: vscode.AuthenticationProviderCreateSessionOptions): Promise<vscode.AuthenticationSession> {
const providerData = this._authenticationProviders.get(providerId);
if (providerData) {
return Promise.resolve(providerData.provider.createSession(scopes));
return Promise.resolve(providerData.provider.createSession(scopes, options));
}

throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { AuthenticationProviderInformation, AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IAuthenticationCreateSessionOptions, AuthenticationProviderInformation, AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
Expand Down Expand Up @@ -742,10 +742,12 @@ export class AuthenticationService extends Disposable implements IAuthentication
}
}

async createSession(id: string, scopes: string[], activateImmediate: boolean = false): Promise<AuthenticationSession> {
const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, activateImmediate);
async createSession(id: string, scopes: string[], options?: IAuthenticationCreateSessionOptions): Promise<AuthenticationSession> {
const authProvider = this._authenticationProviders.get(id) || await this.tryActivateProvider(id, !!options?.activateImmediate);
if (authProvider) {
return await authProvider.createSession(scopes);
return await authProvider.createSession(scopes, {
sessionToRecreate: options?.sessionToRecreate
});
} else {
throw new Error(`No authentication provider '${id}' is currently registered.`);
}
Expand Down
13 changes: 11 additions & 2 deletions src/vs/workbench/services/authentication/common/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export interface AuthenticationProviderInformation {
label: string;
}

export interface IAuthenticationCreateSessionOptions {
sessionToRecreate?: AuthenticationSession;
activateImmediate?: boolean;
}

export const IAuthenticationService = createDecorator<IAuthenticationService>('IAuthenticationService');

export interface IAuthenticationService {
Expand Down Expand Up @@ -62,13 +67,17 @@ export interface IAuthenticationService {
getSessions(id: string, scopes?: string[], activateImmediate?: boolean): Promise<ReadonlyArray<AuthenticationSession>>;
getLabel(providerId: string): string;
supportsMultipleAccounts(providerId: string): boolean;
createSession(providerId: string, scopes: string[], activateImmediate?: boolean): Promise<AuthenticationSession>;
createSession(providerId: string, scopes: string[], options?: IAuthenticationCreateSessionOptions): Promise<AuthenticationSession>;
removeSession(providerId: string, sessionId: string): Promise<void>;

manageTrustedExtensionsForAccount(providerId: string, accountName: string): Promise<void>;
removeAccountSessions(providerId: string, accountName: string, sessions: AuthenticationSession[]): Promise<void>;
}

export interface IAuthenticationProviderCreateSessionOptions {
sessionToRecreate?: AuthenticationSession;
}

export interface IAuthenticationProvider {
readonly id: string;
readonly label: string;
Expand All @@ -77,6 +86,6 @@ export interface IAuthenticationProvider {
manageTrustedExtensions(accountName: string): void;
removeAccountSessions(accountName: string, sessions: AuthenticationSession[]): Promise<void>;
getSessions(scopes?: string[]): Promise<readonly AuthenticationSession[]>;
createSession(scopes: string[]): Promise<AuthenticationSession>;
createSession(scopes: string[], options: IAuthenticationProviderCreateSessionOptions): Promise<AuthenticationSession>;
removeSession(sessionId: string): Promise<void>;
}
7 changes: 5 additions & 2 deletions src/vscode-dts/vscode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15648,6 +15648,9 @@ declare module 'vscode' {
readonly label: string;
}

export interface AuthenticationForceNewSessionOptions {
detail?: string;
}

/**
* Options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}.
Expand Down Expand Up @@ -15698,7 +15701,7 @@ declare module 'vscode' {
*
* This defaults to false.
*/
forceNewSession?: boolean | { detail: string };
forceNewSession?: boolean | AuthenticationForceNewSessionOptions;

/**
* Whether we should show the indication to sign in in the Accounts menu.
Expand Down Expand Up @@ -15848,7 +15851,7 @@ declare module 'vscode' {
* @param options The {@link AuthenticationGetSessionOptions} to use
* @returns A thenable that resolves to an authentication session
*/
export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { forceNewSession: true | { detail: string } }): Thenable<AuthenticationSession>;
export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { forceNewSession: true | AuthenticationForceNewSessionOptions }): Thenable<AuthenticationSession>;

/**
* Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not
Expand Down
33 changes: 33 additions & 0 deletions src/vscode-dts/vscode.proposed.getSessions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ declare module 'vscode' {

// https://github.com/microsoft/vscode/issues/152399

export interface AuthenticationForceNewSessionOptions {
sessionToRecreate?: AuthenticationSession;
}

export namespace authentication {
/**
* Get all authentication sessions matching the desired scopes that this extension has access to. In order to request access,
Expand All @@ -22,4 +26,33 @@ declare module 'vscode' {
*/
export function getSessions(providerId: string, scopes: readonly string[]): Thenable<readonly AuthenticationSession[]>;
}

/**
* The options passed in to the provider when creating a session.
*/
export interface AuthenticationProviderCreateSessionOptions {
/**
* The session that is being asked to be recreated. If this is passed in, the provider should
* attempt to recreate the session based on the information in this session.
*/
sessionToRecreate?: AuthenticationSession;
}

export interface AuthenticationProvider {
/**
* Prompts a user to login.
*
* If login is successful, the onDidChangeSessions event should be fired.
*
* If login fails, a rejected promise should be returned.
*
* If the provider has specified that it does not support multiple accounts,
* then this should never be called if there is already an existing session matching these
* scopes.
* @param scopes A list of scopes, permissions, that the new session should be created with.
* @param options Additional options for creating a session.
* @returns A promise that resolves to an authentication session.
*/
createSession(scopes: readonly string[], options: AuthenticationProviderCreateSessionOptions): Thenable<AuthenticationSession>;
}
}

0 comments on commit 09bf7d9

Please sign in to comment.