| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 | 
							- diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts
 
- index 830e698..cd6f27e 100644
 
- --- a/extensions/github-authentication/src/githubServer.ts
 
- +++ b/extensions/github-authentication/src/githubServer.ts
 
- @@ -7,4 +7,2 @@ import * as vscode from 'vscode';
 
-  import fetch, { Response } from 'node-fetch';
 
- -import { v4 as uuid } from 'uuid';
 
- -import { PromiseAdapter, promiseFromEvent } from './common/utils';
 
-  import { ExperimentationTelemetry } from './experimentationService';
 
- @@ -12,13 +10,4 @@ import { AuthProviderType } from './github';
 
-  import { Log } from './common/logger';
 
- -import { isSupportedEnvironment } from './common/env';
 
- -import { LoopbackAuthServer } from './authServer';
 
- -import path = require('path');
 
- -
 
- -const CLIENT_ID = '01ab8ac9400c4e429b23';
 
- -const GITHUB_TOKEN_URL = 'https://vscode.dev/codeExchangeProxyEndpoints/github/login/oauth/access_token';
 
-  const NETWORK_ERROR = 'network error';
 
-  
 
- -const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect';
 
- -const REDIRECT_URL_INSIDERS = 'https://insiders.vscode.dev/redirect';
 
- -
 
-  class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
 
- @@ -42,9 +31,2 @@ export interface IGitHubServer extends vscode.Disposable {
 
-  
 
- -interface IGitHubDeviceCodeResponse {
 
- -	device_code: string;
 
- -	user_code: string;
 
- -	verification_uri: string;
 
- -	interval: number;
 
- -}
 
- -
 
-  async function getScopes(token: string, serverUri: vscode.Uri, logger: Log): Promise<string[]> {
 
- @@ -55,3 +37,3 @@ async function getScopes(token: string, serverUri: vscode.Uri, logger: Log): Pro
 
-  				Authorization: `token ${token}`,
 
- -				'User-Agent': 'Visual-Studio-Code'
 
- +				'User-Agent': 'VSCodium'
 
-  			}
 
- @@ -75,7 +57,4 @@ export class GitHubServer implements IGitHubServer {
 
-  
 
- -	private _pendingNonces = new Map<string, string[]>();
 
- -	private _codeExchangePromises = new Map<string, { promise: Promise<string>; cancel: vscode.EventEmitter<void> }>();
 
-  	private _disposable: vscode.Disposable | undefined;
 
-  	private static _uriHandler: UriEventHandler | undefined;
 
- -	private _redirectEndpoint: string | undefined;
 
-  
 
- @@ -83,2 +62,3 @@ export class GitHubServer implements IGitHubServer {
 
-  		public readonly type: AuthProviderType,
 
- +		// @ts-ignore
 
-  		private readonly _supportDeviceCodeFlow: boolean,
 
- @@ -102,41 +82,2 @@ export class GitHubServer implements IGitHubServer {
 
-  
 
- -	private async getRedirectEndpoint(): Promise<string> {
 
- -		if (this._redirectEndpoint) {
 
- -			return this._redirectEndpoint;
 
- -		}
 
- -		if (this.type === AuthProviderType.github) {
 
- -			const proxyEndpoints = await vscode.commands.executeCommand<{ [providerId: string]: string } | undefined>('workbench.getCodeExchangeProxyEndpoints');
 
- -			// If we are running in insiders vscode.dev, then ensure we use the redirect route on that.
 
- -			this._redirectEndpoint = REDIRECT_URL_STABLE;
 
- -			if (proxyEndpoints?.github && new URL(proxyEndpoints.github).hostname === 'insiders.vscode.dev') {
 
- -				this._redirectEndpoint = REDIRECT_URL_INSIDERS;
 
- -			}
 
- -			return this._redirectEndpoint;
 
- -		} else {
 
- -			// GHES
 
- -			const result = await fetch(this.getServerUri('/meta').toString(true));
 
- -			if (result.ok) {
 
- -				try {
 
- -					const json: { installed_version: string } = await result.json();
 
- -					const [majorStr, minorStr, _patch] = json.installed_version.split('.');
 
- -					const major = Number(majorStr);
 
- -					const minor = Number(minorStr);
 
- -					if (major >= 4 || major === 3 && minor >= 8
 
- -					) {
 
- -						// GHES 3.8 and above used vscode.dev/redirect as the route.
 
- -						// It only supports a single redirect endpoint, so we can't use
 
- -						// insiders.vscode.dev/redirect when we're running in Insiders, unfortunately.
 
- -						this._redirectEndpoint = 'https://vscode.dev/redirect';
 
- -					}
 
- -				} catch (e) {
 
- -					this._logger.error(e);
 
- -				}
 
- -			}
 
- -
 
- -			// TODO in like 1 year change the default vscode.dev/redirect maybe
 
- -			this._redirectEndpoint = 'https://vscode-auth.github.com/';
 
- -		}
 
- -		return this._redirectEndpoint;
 
- -	}
 
- -
 
-  	dispose() {
 
- @@ -156,58 +97,8 @@ export class GitHubServer implements IGitHubServer {
 
-  		let userCancelled: boolean | undefined;
 
- -		const yes = vscode.l10n.t('Yes');
 
- -		const no = vscode.l10n.t('No');
 
- -		const promptToContinue = async () => {
 
- -			if (userCancelled === undefined) {
 
- -				// We haven't had a failure yet so wait to prompt
 
- -				return;
 
- -			}
 
- -			const message = userCancelled
 
- -				? vscode.l10n.t('Having trouble logging in? Would you like to try a different way?')
 
- -				: vscode.l10n.t('You have not yet finished authorizing this extension to use GitHub. Would you like to keep trying?');
 
- -			const result = await vscode.window.showWarningMessage(message, yes, no);
 
- -			if (result !== yes) {
 
- -				throw new Error('Cancelled');
 
- -			}
 
- -		};
 
- -
 
- -		const nonce = uuid();
 
- -		const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate?nonce=${encodeURIComponent(nonce)}`));
 
-  
 
- -		const supported = isSupportedEnvironment(callbackUri);
 
- -		if (supported) {
 
- -			try {
 
- -				return await this.doLoginWithoutLocalServer(scopes, nonce, callbackUri);
 
- -			} catch (e) {
 
- -				this._logger.error(e);
 
- -				userCancelled = e.message ?? e === 'User Cancelled';
 
- -			}
 
- -		}
 
- -
 
- -		// Starting a local server isn't supported in web
 
- -		if (vscode.env.uiKind === vscode.UIKind.Desktop) {
 
- -			try {
 
- -				await promptToContinue();
 
- -				return await this.doLoginWithLocalServer(scopes);
 
- -			} catch (e) {
 
- -				this._logger.error(e);
 
- -				userCancelled = e.message ?? e === 'User Cancelled';
 
- -			}
 
- -		}
 
- -
 
- -		if (this._supportDeviceCodeFlow) {
 
- -			try {
 
- -				await promptToContinue();
 
- -				return await this.doLoginDeviceCodeFlow(scopes);
 
- -			} catch (e) {
 
- -				this._logger.error(e);
 
- -				userCancelled = e.message ?? e === 'User Cancelled';
 
- -			}
 
- -		} else if (!supported) {
 
- -			try {
 
- -				await promptToContinue();
 
- -				return await this.doLoginWithPat(scopes);
 
- -			} catch (e) {
 
- -				this._logger.error(e);
 
- -				userCancelled = e.message ?? e === 'User Cancelled';
 
- -			}
 
- +		try {
 
- +			return await this.doLoginWithPat(scopes);
 
- +		} catch (e) {
 
- +			this._logger.error(e);
 
- +			userCancelled = e.message ?? e === 'User Cancelled';
 
-  		}
 
- @@ -217,136 +108,2 @@ export class GitHubServer implements IGitHubServer {
 
-  
 
- -	private async doLoginWithoutLocalServer(scopes: string, nonce: string, callbackUri: vscode.Uri): Promise<string> {
 
- -		this._logger.info(`Trying without local server... (${scopes})`);
 
- -		return await vscode.window.withProgress<string>({
 
- -			location: vscode.ProgressLocation.Notification,
 
- -			title: vscode.l10n.t({
 
- -				message: 'Signing in to {0}...',
 
- -				args: [this.baseUri.authority],
 
- -				comment: ['The {0} will be a url, e.g. github.com']
 
- -			}),
 
- -			cancellable: true
 
- -		}, async (_, token) => {
 
- -			const existingNonces = this._pendingNonces.get(scopes) || [];
 
- -			this._pendingNonces.set(scopes, [...existingNonces, nonce]);
 
- -			const redirectUri = await this.getRedirectEndpoint();
 
- -			const searchParams = new URLSearchParams([
 
- -				['client_id', CLIENT_ID],
 
- -				['redirect_uri', redirectUri],
 
- -				['scope', scopes],
 
- -				['state', encodeURIComponent(callbackUri.toString(true))]
 
- -			]);
 
- -
 
- -			const uri = vscode.Uri.parse(this.baseUri.with({
 
- -				path: '/login/oauth/authorize',
 
- -				query: searchParams.toString()
 
- -			}).toString(true));
 
- -			await vscode.env.openExternal(uri);
 
- -
 
- -			// Register a single listener for the URI callback, in case the user starts the login process multiple times
 
- -			// before completing it.
 
- -			let codeExchangePromise = this._codeExchangePromises.get(scopes);
 
- -			if (!codeExchangePromise) {
 
- -				codeExchangePromise = promiseFromEvent(GitHubServer._uriHandler!.event, this.handleUri(scopes));
 
- -				this._codeExchangePromises.set(scopes, codeExchangePromise);
 
- -			}
 
- -
 
- -			try {
 
- -				return await Promise.race([
 
- -					codeExchangePromise.promise,
 
- -					new Promise<string>((_, reject) => setTimeout(() => reject('Timed out'), 300_000)), // 5min timeout
 
- -					promiseFromEvent<any, any>(token.onCancellationRequested, (_, __, reject) => { reject('User Cancelled'); }).promise
 
- -				]);
 
- -			} finally {
 
- -				this._pendingNonces.delete(scopes);
 
- -				codeExchangePromise?.cancel.fire();
 
- -				this._codeExchangePromises.delete(scopes);
 
- -			}
 
- -		});
 
- -	}
 
- -
 
- -	private async doLoginWithLocalServer(scopes: string): Promise<string> {
 
- -		this._logger.info(`Trying with local server... (${scopes})`);
 
- -		return await vscode.window.withProgress<string>({
 
- -			location: vscode.ProgressLocation.Notification,
 
- -			title: vscode.l10n.t({
 
- -				message: 'Signing in to {0}...',
 
- -				args: [this.baseUri.authority],
 
- -				comment: ['The {0} will be a url, e.g. github.com']
 
- -			}),
 
- -			cancellable: true
 
- -		}, async (_, token) => {
 
- -			const redirectUri = await this.getRedirectEndpoint();
 
- -			const searchParams = new URLSearchParams([
 
- -				['client_id', CLIENT_ID],
 
- -				['redirect_uri', redirectUri],
 
- -				['scope', scopes],
 
- -			]);
 
- -
 
- -			const loginUrl = this.baseUri.with({
 
- -				path: '/login/oauth/authorize',
 
- -				query: searchParams.toString()
 
- -			});
 
- -			const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl.toString(true));
 
- -			const port = await server.start();
 
- -
 
- -			let codeToExchange;
 
- -			try {
 
- -				vscode.env.openExternal(vscode.Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`));
 
- -				const { code } = await Promise.race([
 
- -					server.waitForOAuthResponse(),
 
- -					new Promise<any>((_, reject) => setTimeout(() => reject('Timed out'), 300_000)), // 5min timeout
 
- -					promiseFromEvent<any, any>(token.onCancellationRequested, (_, __, reject) => { reject('User Cancelled'); }).promise
 
- -				]);
 
- -				codeToExchange = code;
 
- -			} finally {
 
- -				setTimeout(() => {
 
- -					void server.stop();
 
- -				}, 5000);
 
- -			}
 
- -
 
- -			const accessToken = await this.exchangeCodeForToken(codeToExchange);
 
- -			return accessToken;
 
- -		});
 
- -	}
 
- -
 
- -	private async doLoginDeviceCodeFlow(scopes: string): Promise<string> {
 
- -		this._logger.info(`Trying device code flow... (${scopes})`);
 
- -
 
- -		// Get initial device code
 
- -		const uri = this.baseUri.with({
 
- -			path: '/login/device/code',
 
- -			query: `client_id=${CLIENT_ID}&scope=${scopes}`
 
- -		});
 
- -		const result = await fetch(uri.toString(true), {
 
- -			method: 'POST',
 
- -			headers: {
 
- -				Accept: 'application/json'
 
- -			}
 
- -		});
 
- -		if (!result.ok) {
 
- -			throw new Error(`Failed to get one-time code: ${await result.text()}`);
 
- -		}
 
- -
 
- -		const json = await result.json() as IGitHubDeviceCodeResponse;
 
- -
 
- -		const button = vscode.l10n.t('Copy & Continue to GitHub');
 
- -		const modalResult = await vscode.window.showInformationMessage(
 
- -			vscode.l10n.t({ message: 'Your Code: {0}', args: [json.user_code], comment: ['The {0} will be a code, e.g. 123-456'] }),
 
- -			{
 
- -				modal: true,
 
- -				detail: vscode.l10n.t('To finish authenticating, navigate to GitHub and paste in the above one-time code.')
 
- -			}, button);
 
- -
 
- -		if (modalResult !== button) {
 
- -			throw new Error('User Cancelled');
 
- -		}
 
- -
 
- -		await vscode.env.clipboard.writeText(json.user_code);
 
- -
 
- -		const uriToOpen = await vscode.env.asExternalUri(vscode.Uri.parse(json.verification_uri));
 
- -		await vscode.env.openExternal(uriToOpen);
 
- -
 
- -		return await this.waitForDeviceCodeAccessToken(json);
 
- -	}
 
- -
 
-  	private async doLoginWithPat(scopes: string): Promise<string> {
 
- @@ -374,124 +131,2 @@ export class GitHubServer implements IGitHubServer {
 
-  
 
- -	private async waitForDeviceCodeAccessToken(
 
- -		json: IGitHubDeviceCodeResponse,
 
- -	): Promise<string> {
 
- -		return await vscode.window.withProgress<string>({
 
- -			location: vscode.ProgressLocation.Notification,
 
- -			cancellable: true,
 
- -			title: vscode.l10n.t({
 
- -				message: 'Open [{0}]({0}) in a new tab and paste your one-time code: {1}',
 
- -				args: [json.verification_uri, json.user_code],
 
- -				comment: [
 
- -					'The [{0}]({0}) will be a url and the {1} will be a code, e.g. 123-456',
 
- -					'{Locked="[{0}]({0})"}'
 
- -				]
 
- -			})
 
- -		}, async (_, token) => {
 
- -			const refreshTokenUri = this.baseUri.with({
 
- -				path: '/login/oauth/access_token',
 
- -				query: `client_id=${CLIENT_ID}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code`
 
- -			});
 
- -
 
- -			// Try for 2 minutes
 
- -			const attempts = 120 / json.interval;
 
- -			for (let i = 0; i < attempts; i++) {
 
- -				await new Promise(resolve => setTimeout(resolve, json.interval * 1000));
 
- -				if (token.isCancellationRequested) {
 
- -					throw new Error('User Cancelled');
 
- -				}
 
- -				let accessTokenResult;
 
- -				try {
 
- -					accessTokenResult = await fetch(refreshTokenUri.toString(true), {
 
- -						method: 'POST',
 
- -						headers: {
 
- -							Accept: 'application/json'
 
- -						}
 
- -					});
 
- -				} catch {
 
- -					continue;
 
- -				}
 
- -
 
- -				if (!accessTokenResult.ok) {
 
- -					continue;
 
- -				}
 
- -
 
- -				const accessTokenJson = await accessTokenResult.json();
 
- -
 
- -				if (accessTokenJson.error === 'authorization_pending') {
 
- -					continue;
 
- -				}
 
- -
 
- -				if (accessTokenJson.error) {
 
- -					throw new Error(accessTokenJson.error_description);
 
- -				}
 
- -
 
- -				return accessTokenJson.access_token;
 
- -			}
 
- -
 
- -			throw new Error('Cancelled');
 
- -		});
 
- -	}
 
- -
 
- -	private handleUri: (scopes: string) => PromiseAdapter<vscode.Uri, string> =
 
- -		(scopes) => (uri, resolve, reject) => {
 
- -			const query = new URLSearchParams(uri.query);
 
- -			const code = query.get('code');
 
- -			const nonce = query.get('nonce');
 
- -			if (!code) {
 
- -				reject(new Error('No code'));
 
- -				return;
 
- -			}
 
- -			if (!nonce) {
 
- -				reject(new Error('No nonce'));
 
- -				return;
 
- -			}
 
- -
 
- -			const acceptedNonces = this._pendingNonces.get(scopes) || [];
 
- -			if (!acceptedNonces.includes(nonce)) {
 
- -				// A common scenario of this happening is if you:
 
- -				// 1. Trigger a sign in with one set of scopes
 
- -				// 2. Before finishing 1, you trigger a sign in with a different set of scopes
 
- -				// In this scenario we should just return and wait for the next UriHandler event
 
- -				// to run as we are probably still waiting on the user to hit 'Continue'
 
- -				this._logger.info('Nonce not found in accepted nonces. Skipping this execution...');
 
- -				return;
 
- -			}
 
- -
 
- -			resolve(this.exchangeCodeForToken(code));
 
- -		};
 
- -
 
- -	private async exchangeCodeForToken(code: string): Promise<string> {
 
- -		this._logger.info('Exchanging code for token...');
 
- -
 
- -		const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints');
 
- -		const endpointUrl = proxyEndpoints?.github ? `${proxyEndpoints.github}login/oauth/access_token` : GITHUB_TOKEN_URL;
 
- -
 
- -		const body = new URLSearchParams([['code', code]]);
 
- -		if (this.type === AuthProviderType.githubEnterprise) {
 
- -			body.append('github_enterprise', this.baseUri.toString(true));
 
- -			body.append('redirect_uri', await this.getRedirectEndpoint());
 
- -		}
 
- -		const result = await fetch(endpointUrl, {
 
- -			method: 'POST',
 
- -			headers: {
 
- -				Accept: 'application/json',
 
- -				'Content-Type': 'application/x-www-form-urlencoded',
 
- -				'Content-Length': body.toString()
 
- -
 
- -			},
 
- -			body: body.toString()
 
- -		});
 
- -
 
- -		if (result.ok) {
 
- -			const json = await result.json();
 
- -			this._logger.info('Token exchange success!');
 
- -			return json.access_token;
 
- -		} else {
 
- -			const text = await result.text();
 
- -			const error = new Error(text);
 
- -			error.name = 'GitHubTokenExchangeError';
 
- -			throw error;
 
- -		}
 
- -	}
 
- -
 
-  	private getServerUri(path: string = '') {
 
- diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
 
- index ce2a3e8..2a5cf60 100644
 
- --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
 
- +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
 
- @@ -306,3 +306,3 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem {
 
-  
 
- -		if (providers.length && !menus.length) {
 
- +		if (!menus.length) {
 
-  			const noAccountsAvailableAction = disposables.add(new Action('noAccountsAvailable', localize('noAccounts', "You are not signed in to any accounts"), undefined, false));
 
- diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts
 
- index d5e2b75..a996eb7 100644
 
- --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts
 
- +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts
 
- @@ -279,12 +279,2 @@ export class AuthenticationService extends Disposable implements IAuthentication
 
-  		}
 
- -
 
- -		if (!this._authenticationProviders.size) {
 
- -			placeholderMenuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
 
- -				command: {
 
- -					id: 'noAuthenticationProviders',
 
- -					title: nls.localize('loading', "Loading..."),
 
- -					precondition: ContextKeyExpr.false()
 
- -				},
 
- -			});
 
- -		}
 
-  	}
 
 
  |