extensions/github-authentication/src/githubServer.ts/GitHubServer/login
class GitHubServer:	public async login(scopes: string): Promise<string> {
		Logger.info('Logging in...');
		this.updateStatusBarItem(true);

		const state = uuid();

		// TODO@joaomoreno TODO@RMacfarlane
		const nocors = await this.isNoCorsEnvironment();
		const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate${nocors ? '?nocors=true' : ''}`));

		if (this.isTestEnvironment(callbackUri)) {
			const token = await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true });
			if (!token) { throw new Error('Sign in failed: No token provided'); }

			const tokenScopes = await this.getScopes(token); // Example: ['repo', 'user']
			const scopesList = scopes.split(' '); // Example: 'read:user repo user:email'
			if (!scopesList.every(scope => {
				const included = tokenScopes.includes(scope);
				if (included || !scope.includes(':')) {
					return included;
				}

				return scope.split(':').some(splitScopes => {
					return tokenScopes.includes(splitScopes);
				});
			})) {
				throw new Error(`The provided token is does not match the requested scopes: ${scopes}`);
			}

			this.updateStatusBarItem(false);
			return token;
		} else {
			const existingStates = this._pendingStates.get(scopes) || [];
			this._pendingStates.set(scopes, [...existingStates, state]);

			const uri = vscode.Uri.parse(`https://${AUTH_RELAY_SERVER}/authorize/?callbackUri=${encodeURIComponent(callbackUri.toString())}&scope=${scopes}&state=${state}&responseType=code&authServer=https://github.com${nocors ? '&nocors=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(uriHandler.event, this.exchangeCodeForToken(scopes));
			this._codeExchangePromises.set(scopes, codeExchangePromise);
		}

		return Promise.race([
			codeExchangePromise.promise,
			promiseFromEvent<string | undefined, string>(this._onDidManuallyProvideToken.event, (token: string | undefined, resolve, reject): void => {
				if (!token) {
					reject('Cancelled');
				} else {
					resolve(token);
				}
			}).promise
		]).finally(() => {
			this._pendingStates.delete(scopes);
			codeExchangePromise?.cancel.fire();
			this._codeExchangePromises.delete(scopes);
			this.updateStatusBarItem(false);
		});
	}