extHostAuthentication.ts 5.7 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import type * as vscode from 'vscode';
7 8 9
import * as modes from 'vs/editor/common/modes';
import { Emitter, Event } from 'vs/base/common/event';
import { IMainContext, MainContext, MainThreadAuthenticationShape, ExtHostAuthenticationShape } from 'vs/workbench/api/common/extHost.protocol';
10 11
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
12

13
export class AuthenticationProviderWrapper implements vscode.AuthenticationProvider {
14
	readonly onDidChangeSessions: vscode.Event<void>;
15

16 17
	constructor(private _requestingExtension: IExtensionDescription,
		private _provider: vscode.AuthenticationProvider,
18
		private _proxy: MainThreadAuthenticationShape) {
19 20 21 22 23 24 25 26 27 28

		this.onDidChangeSessions = this._provider.onDidChangeSessions;
	}

	get id(): string {
		return this._provider.id;
	}

	get displayName(): string {
		return this._provider.displayName;
29 30
	}

31
	async getSessions(): Promise<ReadonlyArray<vscode.AuthenticationSession>> {
32 33 34 35 36
		return (await this._provider.getSessions()).map(session => {
			return {
				id: session.id,
				accountName: session.accountName,
				scopes: session.scopes,
37
				getAccessToken: async () => {
38 39 40 41 42 43 44 45 46 47
					const isAllowed = await this._proxy.$getSessionsPrompt(
						this._provider.id,
						this.displayName,
						ExtensionIdentifier.toKey(this._requestingExtension.identifier),
						this._requestingExtension.displayName || this._requestingExtension.name);

					if (!isAllowed) {
						throw new Error('User did not consent to token access.');
					}

48
					return session.getAccessToken();
49 50 51
				}
			};
		});
52 53
	}

54
	async login(scopes: string[]): Promise<vscode.AuthenticationSession> {
55 56 57 58 59
		const isAllowed = await this._proxy.$loginPrompt(this._provider.id, this.displayName, ExtensionIdentifier.toKey(this._requestingExtension.identifier), this._requestingExtension.displayName || this._requestingExtension.name);
		if (!isAllowed) {
			throw new Error('User did not consent to login.');
		}

60
		return this._provider.login(scopes);
61 62
	}

63
	logout(sessionId: string): Thenable<void> {
64
		return this._provider.logout(sessionId);
65 66 67 68 69
	}
}

export class ExtHostAuthentication implements ExtHostAuthenticationShape {
	private _proxy: MainThreadAuthenticationShape;
70
	private _authenticationProviders: Map<string, vscode.AuthenticationProvider> = new Map<string, vscode.AuthenticationProvider>();
71

72 73
	private _onDidChangeAuthenticationProviders = new Emitter<vscode.AuthenticationProvidersChangeEvent>();
	readonly onDidChangeAuthenticationProviders: Event<vscode.AuthenticationProvidersChangeEvent> = this._onDidChangeAuthenticationProviders.event;
74 75 76

	constructor(mainContext: IMainContext) {
		this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication);
77
	}
78

79 80 81 82
	providers(requestingExtension: IExtensionDescription): vscode.AuthenticationProvider[] {
		let providers: vscode.AuthenticationProvider[] = [];
		this._authenticationProviders.forEach(provider => providers.push(new AuthenticationProviderWrapper(requestingExtension, provider, this._proxy)));
		return providers;
83
	}
84

85
	registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
86 87 88 89
		if (this._authenticationProviders.get(provider.id)) {
			throw new Error(`An authentication provider with id '${provider.id}' is already registered.`);
		}

90 91 92 93 94
		this._authenticationProviders.set(provider.id, provider);

		const listener = provider.onDidChangeSessions(_ => {
			this._proxy.$onDidChangeSessions(provider.id);
		});
95

96
		this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName);
97
		this._onDidChangeAuthenticationProviders.fire({ added: [provider.id], removed: [] });
98 99 100 101 102

		return new Disposable(() => {
			listener.dispose();
			this._authenticationProviders.delete(provider.id);
			this._proxy.$unregisterAuthenticationProvider(provider.id);
103
			this._onDidChangeAuthenticationProviders.fire({ added: [], removed: [provider.id] });
104
		});
105 106
	}

107
	$login(providerId: string, scopes: string[]): Promise<modes.AuthenticationSession> {
108
		const authProvider = this._authenticationProviders.get(providerId);
109
		if (authProvider) {
110
			return Promise.resolve(authProvider.login(scopes));
111 112
		}

113
		throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
114 115
	}

116 117
	$logout(providerId: string, sessionId: string): Promise<void> {
		const authProvider = this._authenticationProviders.get(providerId);
118
		if (authProvider) {
119
			return Promise.resolve(authProvider.logout(sessionId));
120 121
		}

122
		throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
123 124
	}

125
	$getSessions(providerId: string): Promise<ReadonlyArray<modes.AuthenticationSession>> {
126
		const authProvider = this._authenticationProviders.get(providerId);
127
		if (authProvider) {
128
			return Promise.resolve(authProvider.getSessions());
129 130
		}

131 132 133 134 135 136 137 138 139
		throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
	}

	async $getSessionAccessToken(providerId: string, sessionId: string): Promise<string> {
		const authProvider = this._authenticationProviders.get(providerId);
		if (authProvider) {
			const sessions = await authProvider.getSessions();
			const session = sessions.find(session => session.id === sessionId);
			if (session) {
140
				return session.getAccessToken();
141 142 143 144 145 146
			}

			throw new Error(`Unable to find session with id: ${sessionId}`);
		}

		throw new Error(`Unable to find authentication provider with handle: ${providerId}`);
147 148
	}
}