提交 b80e8133 编写于 作者: R Rachel Macfarlane

Updates to authentication provider API

上级 2dcd10f9
......@@ -23,7 +23,7 @@ interface IToken {
refreshToken: string;
}
export const onDidChangeAccounts = new vscode.EventEmitter<vscode.Account[]>();
export const onDidChangeSessions = new vscode.EventEmitter<void>();
export class AzureActiveDirectoryService {
private _token: IToken | undefined;
......@@ -44,20 +44,20 @@ export class AzureActiveDirectoryService {
// Another window has logged in, generate access token for this instance.
if (refreshToken && !this._token) {
await this.refreshToken(refreshToken);
onDidChangeAccounts.fire(this.accounts);
onDidChangeSessions.fire();
}
// Another window has logged out
if (!refreshToken && this._token) {
await this.logout();
onDidChangeAccounts.fire(this.accounts);
onDidChangeSessions.fire();
}
this.pollForChange();
}, 1000 * 30);
}
private tokenToAccount(token: IToken): vscode.Account {
private tokenToAccount(token: IToken): vscode.Session {
return {
id: '',
accessToken: token.accessToken,
......@@ -77,7 +77,7 @@ export class AzureActiveDirectoryService {
return displayName;
}
get accounts(): vscode.Account[] {
get sessions(): vscode.Session[] {
return this._token ? [this.tokenToAccount(this._token)] : [];
}
......@@ -146,7 +146,7 @@ export class AzureActiveDirectoryService {
} catch (e) {
await this.logout();
} finally {
onDidChangeAccounts.fire(this.accounts);
onDidChangeSessions.fire();
}
}, 1000 * (parseInt(token.expiresIn) - 10));
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { AzureActiveDirectoryService, onDidChangeAccounts } from './AADHelper';
import { AzureActiveDirectoryService, onDidChangeSessions } from './AADHelper';
export async function activate(context: vscode.ExtensionContext) {
......@@ -15,12 +15,12 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.authentication.registerAuthenticationProvider({
id: 'MSA',
displayName: 'Microsoft Account', // TODO localize
onDidChangeAccounts: onDidChangeAccounts.event,
getAccounts: () => Promise.resolve(loginService.accounts),
onDidChangeSessions: onDidChangeSessions.event,
getSessions: () => Promise.resolve(loginService.sessions),
login: async () => {
try {
await loginService.login();
return loginService.accounts[0]!;
return loginService.sessions[0]!;
} catch (e) {
vscode.window.showErrorMessage(`Logging in failed: ${e}`);
throw e;
......
......@@ -16,25 +16,45 @@
declare module 'vscode' {
export interface Account {
readonly id: string;
readonly accessToken: string;
readonly displayName: string;
export interface Session {
id: string;
accessToken: string;
displayName: string;
}
export interface AuthenticationProvider {
readonly id: string;
readonly displayName: string;
readonly onDidChangeSessions: Event<void>;
getAccounts(): Promise<ReadonlyArray<Account>>;
readonly onDidChangeAccounts: Event<ReadonlyArray<Account>>;
/**
* Returns an array of current sessions.
*/
getSessions(): Promise<ReadonlyArray<Session>>;
login(): Promise<Account>;
logout(accountId: string): Promise<void>;
/**
* Prompts a user to login.
*/
login(): Promise<Session>;
logout(sessionId: string): Promise<void>;
}
export namespace authentication {
export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable;
/**
* Fires with the provider id that was registered or unregistered.
*/
export const onDidRegisterAuthenticationProvider: Event<string>;
export const onDidUnregisterAuthenticationProvider: Event<string>;
/**
* Fires with the provider id that changed sessions.
*/
export const onDidChangeSessions: Event<string>;
export function login(providerId: string): Promise<Session>;
export function logout(providerId: string, accountId: string): Promise<void>;
export function getSessions(providerId: string): Promise<ReadonlyArray<Session> | undefined>;
}
// #region Ben - extension auth flow (desktop+web)
......
......@@ -1283,23 +1283,12 @@ export interface RenameProvider {
/**
* @internal
*/
export interface Account {
export interface Session {
id: string;
accessToken: string;
displayName: string;
}
/**
* @internal
*/
export interface AuthenticationProvider {
getAccount(): Promise<Account | undefined>;
onDidChangeAccount: Event<Account>;
login(): Promise<Account>;
logout(accountId: string): Promise<void>;
}
export interface Command {
id: string;
title: string;
......
......@@ -16,25 +16,45 @@
declare module 'vscode' {
export interface Account {
readonly id: string;
readonly accessToken: string;
readonly displayName: string;
export interface Session {
id: string;
accessToken: string;
displayName: string;
}
export interface AuthenticationProvider {
readonly id: string;
readonly displayName: string;
readonly onDidChangeSessions: Event<void>;
getAccounts(): Promise<ReadonlyArray<Account>>;
readonly onDidChangeAccounts: Event<ReadonlyArray<Account>>;
/**
* Returns an array of current sessions.
*/
getSessions(): Promise<ReadonlyArray<Session>>;
login(): Promise<Account>;
logout(accountId: string): Promise<void>;
/**
* Prompts a user to login.
*/
login(): Promise<Session>;
logout(sessionId: string): Promise<void>;
}
export namespace authentication {
export function registerAuthenticationProvider(provider: AuthenticationProvider): Disposable;
/**
* Fires with the provider id that was registered or unregistered.
*/
export const onDidRegisterAuthenticationProvider: Event<string>;
export const onDidUnregisterAuthenticationProvider: Event<string>;
/**
* Fires with the provider id that changed sessions.
*/
export const onDidChangeSessions: Event<string>;
export function login(providerId: string): Promise<Session>;
export function logout(providerId: string, accountId: string): Promise<void>;
export function getSessions(providerId: string): Promise<ReadonlyArray<Session>>;
}
//#region Alex - resolvers
......
......@@ -10,32 +10,27 @@ import { IAuthenticationService } from 'vs/workbench/services/authentication/bro
import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol';
export class MainThreadAuthenticationProvider {
public readonly handle: number;
constructor(
private readonly _proxy: ExtHostAuthenticationShape,
public readonly id: string,
handle: number
) {
this.handle = handle;
}
public readonly id: string
) { }
accounts(): Promise<ReadonlyArray<modes.Account>> {
return this._proxy.$accounts(this.handle);
getSessions(): Promise<ReadonlyArray<modes.Session>> {
return this._proxy.$getSessions(this.id);
}
login(): Promise<modes.Account> {
return this._proxy.$login(this.handle);
login(): Promise<modes.Session> {
return this._proxy.$login(this.id);
}
logout(accountId: string): Promise<void> {
return this._proxy.$logout(this.handle, accountId);
return this._proxy.$logout(this.id, accountId);
}
}
@extHostNamedCustomer(MainContext.MainThreadAuthentication)
export class MainThreadAuthentication extends Disposable implements MainThreadAuthenticationShape {
private readonly _proxy: ExtHostAuthenticationShape;
private _handlers = new Map<number, string>();
constructor(
extHostContext: IExtHostContext,
......@@ -45,25 +40,16 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
}
$registerAuthenticationProvider(handle: number, id: string): void {
const provider = new MainThreadAuthenticationProvider(this._proxy, id, handle);
this._handlers.set(handle, id);
$registerAuthenticationProvider(id: string): void {
const provider = new MainThreadAuthenticationProvider(this._proxy, id);
this.authenticationService.registerAuthenticationProvider(id, provider);
}
$unregisterAuthenticationProvider(handle: number): void {
const id = this._handlers.get(handle);
if (!id) {
throw new Error(`No authentication provider registered with id ${id}`);
}
$unregisterAuthenticationProvider(id: string): void {
this.authenticationService.unregisterAuthenticationProvider(id);
}
$onDidChangeAccounts(handle: number, accounts: ReadonlyArray<modes.Account>) {
const id = this._handlers.get(handle);
if (id) {
this.authenticationService.accountsUpdate(id, accounts);
}
$onDidChangeSessions(id: string) {
this.authenticationService.sessionsUpdate(id);
}
}
......@@ -182,6 +182,24 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const authentication: typeof vscode.authentication = {
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
return extHostAuthentication.registerAuthenticationProvider(provider);
},
login(providerId: string): Promise<vscode.Session> {
return extHostAuthentication.$login(providerId);
},
logout(providerId: string, accountId: string): Promise<void> {
return extHostAuthentication.$logout(providerId, accountId);
},
getSessions(providerId: string): Promise<ReadonlyArray<vscode.Session>> {
return extHostAuthentication.$getSessions(providerId);
},
get onDidChangeSessions() {
return extHostAuthentication.onDidChangeSessions;
},
get onDidRegisterAuthenticationProvider() {
return extHostAuthentication.onDidRegisterAuthenticationProvider;
},
get onDidUnregisterAuthenticationProvider() {
return extHostAuthentication.onDidUnregisterAuthenticationProvider;
}
};
......
......@@ -148,9 +148,9 @@ export interface MainThreadCommentsShape extends IDisposable {
}
export interface MainThreadAuthenticationShape extends IDisposable {
$registerAuthenticationProvider(handle: number, id: string): void;
$unregisterAuthenticationProvider(handle: number): void;
$onDidChangeAccounts(handle: number, accounts: ReadonlyArray<modes.Account>): void;
$registerAuthenticationProvider(id: string): void;
$unregisterAuthenticationProvider(id: string): void;
$onDidChangeSessions(id: string): void;
}
export interface MainThreadConfigurationShape extends IDisposable {
......@@ -898,10 +898,9 @@ export interface ExtHostLabelServiceShape {
}
export interface ExtHostAuthenticationShape {
$accounts(handle: number): Promise<ReadonlyArray<modes.Account>>;
$login(handle: number): Promise<modes.Account>;
$logout(handle: number, accountId: string): Promise<void>;
// TODO rmacfarlane
$getSessions(id: string): Promise<ReadonlyArray<modes.Session>>;
$login(id: string): Promise<modes.Session>;
$logout(id: string, accountId: string): Promise<void>;
}
export interface ExtHostSearchShape {
......
......@@ -9,75 +9,93 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IMainContext, MainContext, MainThreadAuthenticationShape, ExtHostAuthenticationShape } from 'vs/workbench/api/common/extHost.protocol';
import { IDisposable } from 'vs/base/common/lifecycle';
const _onDidUnregisterAuthenticationProvider = new Emitter<string>();
const _onDidChangeSessions = new Emitter<string>();
export class ExtHostAuthenticationProvider implements IDisposable {
constructor(private _provider: vscode.AuthenticationProvider,
private readonly _handle: number,
private _id: string,
private _proxy: MainThreadAuthenticationShape) {
this._provider.onDidChangeAccounts(x => this._proxy.$onDidChangeAccounts(this._handle, x));
this._provider.onDidChangeSessions(x => {
this._proxy.$onDidChangeSessions(this._id);
_onDidChangeSessions.fire(this._id);
});
}
getAccounts(): Promise<ReadonlyArray<vscode.Account>> {
return this._provider.getAccounts();
getSessions(): Promise<ReadonlyArray<vscode.Session>> {
return this._provider.getSessions();
}
login(): Promise<vscode.Account> {
login(): Promise<vscode.Session> {
return this._provider.login();
}
logout(accountId: string): Promise<void> {
return this._provider.logout(accountId);
logout(sessionId: string): Promise<void> {
return this._provider.logout(sessionId);
}
dispose(): void {
this._proxy.$unregisterAuthenticationProvider(this._handle);
this._proxy.$unregisterAuthenticationProvider(this._id);
_onDidUnregisterAuthenticationProvider.fire(this._id);
}
}
export class ExtHostAuthentication implements ExtHostAuthenticationShape {
public static _handlePool: number = 0;
private _proxy: MainThreadAuthenticationShape;
private _authenticationProviders: Map<number, ExtHostAuthenticationProvider> = new Map<number, ExtHostAuthenticationProvider>();
private _authenticationProviders: Map<string, ExtHostAuthenticationProvider> = new Map<string, ExtHostAuthenticationProvider>();
private _onDidRegisterAuthenticationProvider = new Emitter<string>();
readonly onDidRegisterAuthenticationProvider: Event<string> = this._onDidRegisterAuthenticationProvider.event;
readonly onDidUnregisterAuthenticationProvider: Event<string> = _onDidUnregisterAuthenticationProvider.event;
readonly onDidChangeSessions: Event<string> = _onDidChangeSessions.event;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication);
}
private readonly _onDidRefreshToken = new Emitter<any>();
readonly onDidRefreshToken: Event<any> = this._onDidRefreshToken.event;
this.onDidUnregisterAuthenticationProvider(providerId => {
this._authenticationProviders.delete(providerId);
});
}
registerAuthenticationProvider(provider: vscode.AuthenticationProvider) {
const handle = ExtHostAuthentication._handlePool++;
const authenticationProvider = new ExtHostAuthenticationProvider(provider, handle, this._proxy);
this._authenticationProviders.set(handle, authenticationProvider);
if (this._authenticationProviders.get(provider.id)) {
throw new Error(`An authentication provider with id '${provider.id}' is already registered.`);
}
const authenticationProvider = new ExtHostAuthenticationProvider(provider, provider.id, this._proxy);
this._authenticationProviders.set(provider.id, authenticationProvider);
this._proxy.$registerAuthenticationProvider(handle, provider.id);
this._proxy.$registerAuthenticationProvider(provider.id);
this._onDidRegisterAuthenticationProvider.fire(provider.id);
return authenticationProvider;
}
$accounts(handle: number): Promise<ReadonlyArray<modes.Account>> {
const authProvider = this._authenticationProviders.get(handle);
$login(providerId: string): Promise<modes.Session> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return Promise.resolve(authProvider.getAccounts());
return Promise.resolve(authProvider.login());
}
throw new Error(`Unable to find authentication provider with handle: ${handle}`);
throw new Error(`Unable to find authentication provider with handle: ${0}`);
}
$login(handle: number): Promise<modes.Account> {
const authProvider = this._authenticationProviders.get(handle);
$logout(providerId: string, sessionId: string): Promise<void> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return authProvider.login();
return Promise.resolve(authProvider.logout(sessionId));
}
throw new Error(`Unable to find authentication provider with handle: ${handle}`);
throw new Error(`Unable to find authentication provider with handle: ${0}`);
}
$logout(handle: number, accountId: string): Promise<void> {
const authProvider = this._authenticationProviders.get(handle);
$getSessions(providerId: string): Promise<ReadonlyArray<modes.Session>> {
const authProvider = this._authenticationProviders.get(providerId);
if (authProvider) {
return authProvider.logout(accountId);
return Promise.resolve(authProvider.getSessions());
}
throw new Error(`Unable to find authentication provider with handle: ${handle}`);
throw new Error(`Unable to find authentication provider with handle: ${0}`);
}
}
......@@ -33,8 +33,8 @@ import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/u
import { timeout } from 'vs/base/common/async';
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
import { IAuthenticationService, ChangeAccountEventData } from 'vs/workbench/services/authentication/browser/authenticationService';
import { Account } from 'vs/editor/common/modes';
import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { Session } from 'vs/editor/common/modes';
import { isPromiseCanceledError } from 'vs/base/common/errors';
const enum MSAAuthStatus {
......@@ -58,7 +58,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
private readonly badgeDisposable = this._register(new MutableDisposable());
private readonly conflictsWarningDisposable = this._register(new MutableDisposable());
private readonly signInNotificationDisposable = this._register(new MutableDisposable());
private _activeAccount: Account | undefined;
private _activeAccount: Session | undefined;
constructor(
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
......@@ -89,7 +89,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING))(() => this.onDidChangeEnablement()));
this._register(this.authenticationService.onDidRegisterAuthenticationProvider(e => this.onDidRegisterAuthenticationProvider(e)));
this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(e => this.onDidUnregisterAuthenticationProvider(e)));
this._register(this.authenticationService.onDidChangeAccounts(e => this.onDidChangeAccounts(e)));
this._register(this.authenticationService.onDidChangeSessions(e => this.onDidChangeSessions(e)));
this.registerActions();
this.initializeActiveAccount().then(_ => {
if (isWeb) {
......@@ -102,7 +102,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
private async initializeActiveAccount(): Promise<void> {
const accounts = await this.authenticationService.getAccounts(MSA);
const accounts = await this.authenticationService.getSessions(MSA);
// MSA provider has not yet been registered
if (!accounts) {
return;
......@@ -130,11 +130,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
}
get activeAccount(): Account | undefined {
get activeAccount(): Session | undefined {
return this._activeAccount;
}
set activeAccount(account: Account | undefined) {
set activeAccount(account: Session | undefined) {
this._activeAccount = account;
if (account) {
......@@ -148,11 +148,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
this.updateBadge();
}
private onDidChangeAccounts(event: ChangeAccountEventData): void {
if (event.providerId === MSA) {
private async onDidChangeSessions(providerId: string): Promise<void> {
if (providerId === MSA) {
if (this.activeAccount) {
// Try to update existing account, case where access token has been refreshed
const matchingAccount = event.accounts.filter(a => a.id === this.activeAccount?.id)[0];
const accounts = (await this.authenticationService.getSessions(MSA) || []);
const matchingAccount = accounts.filter(a => a.id === this.activeAccount?.id)[0];
this.activeAccount = matchingAccount;
} else {
this.initializeActiveAccount();
......
......@@ -5,31 +5,26 @@
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Account } from 'vs/editor/common/modes';
import { Session } from 'vs/editor/common/modes';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { MainThreadAuthenticationProvider } from 'vs/workbench/api/browser/mainThreadAuthentication';
export const IAuthenticationService = createDecorator<IAuthenticationService>('IAuthenticationService');
export interface ChangeAccountEventData {
providerId: string;
accounts: ReadonlyArray<Account>;
}
export interface IAuthenticationService {
_serviceBrand: undefined;
registerAuthenticationProvider(id: string, provider: MainThreadAuthenticationProvider): void;
unregisterAuthenticationProvider(id: string): void;
accountsUpdate(providerId: string, accounts: ReadonlyArray<Account>): void;
sessionsUpdate(providerId: string): void;
readonly onDidRegisterAuthenticationProvider: Event<string>;
readonly onDidUnregisterAuthenticationProvider: Event<string>;
readonly onDidChangeAccounts: Event<ChangeAccountEventData>;
getAccounts(providerId: string): Promise<ReadonlyArray<Account> | undefined>;
login(providerId: string): Promise<Account>;
readonly onDidChangeSessions: Event<string>;
getSessions(providerId: string): Promise<ReadonlyArray<Session> | undefined>;
login(providerId: string): Promise<Session>;
logout(providerId: string, accountId: string): Promise<void>;
}
......@@ -44,8 +39,8 @@ export class AuthenticationService extends Disposable implements IAuthentication
private _onDidUnregisterAuthenticationProvider: Emitter<string> = this._register(new Emitter<string>());
readonly onDidUnregisterAuthenticationProvider: Event<string> = this._onDidUnregisterAuthenticationProvider.event;
private _onDidChangeAccounts: Emitter<ChangeAccountEventData> = this._register(new Emitter<ChangeAccountEventData>());
readonly onDidChangeAccounts: Event<ChangeAccountEventData> = this._onDidChangeAccounts.event;
private _onDidChangeSessions: Emitter<string> = this._register(new Emitter<string>());
readonly onDidChangeSessions: Event<string> = this._onDidChangeSessions.event;
constructor() {
super();
......@@ -61,20 +56,20 @@ export class AuthenticationService extends Disposable implements IAuthentication
this._onDidUnregisterAuthenticationProvider.fire(id);
}
accountsUpdate(providerId: string, accounts: ReadonlyArray<Account>): void {
this._onDidChangeAccounts.fire({ providerId, accounts });
sessionsUpdate(id: string): void {
this._onDidChangeSessions.fire(id);
}
async getAccounts(id: string): Promise<ReadonlyArray<Account> | undefined> {
async getSessions(id: string): Promise<ReadonlyArray<Session> | undefined> {
const authProvider = this._authenticationProviders.get(id);
if (authProvider) {
return await authProvider.accounts();
return await authProvider.getSessions();
}
return undefined;
}
async login(id: string): Promise<Account> {
async login(id: string): Promise<Session> {
const authProvider = this._authenticationProviders.get(id);
if (authProvider) {
return authProvider.login();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册