Make sure the same GitHub account is used until we support multiple GH accounts (#206847)

Fixes https://github.com/microsoft/vscode/issues/203850
This commit is contained in:
Tyler James Leonhardt
2024-03-04 15:01:51 -08:00
committed by GitHub
parent 5abb308447
commit 191be39e5a
4 changed files with 53 additions and 12 deletions

View File

@@ -8,3 +8,7 @@ export const TIMED_OUT_ERROR = 'Timed out';
// These error messages are internal and should not be shown to the user in any way.
export const USER_CANCELLATION_ERROR = 'User Cancelled';
export const NETWORK_ERROR = 'network error';
// This is the error message that we throw if the login was cancelled for any reason. Extensions
// calling `getSession` can handle this error to know that the user cancelled the login.
export const CANCELLATION_ERROR = 'Cancelled';

View File

@@ -68,6 +68,7 @@ interface IFlowTriggerOptions {
callbackUri: Uri;
uriHandler: UriEventHandler;
enterpriseUri?: Uri;
existingLogin?: string;
}
interface IFlow {
@@ -149,7 +150,8 @@ const allFlows: IFlow[] = [
nonce,
callbackUri,
uriHandler,
enterpriseUri
enterpriseUri,
existingLogin
}: IFlowTriggerOptions): Promise<string> {
logger.info(`Trying without local server... (${scopes})`);
return await window.withProgress<string>({
@@ -169,6 +171,9 @@ const allFlows: IFlow[] = [
['scope', scopes],
['state', encodeURIComponent(callbackUri.toString(true))]
]);
if (existingLogin) {
searchParams.append('login', existingLogin);
}
// The extra toString, parse is apparently needed for env.openExternal
// to open the correct URL.
@@ -215,7 +220,8 @@ const allFlows: IFlow[] = [
baseUri,
redirectUri,
logger,
enterpriseUri
enterpriseUri,
existingLogin
}: IFlowTriggerOptions): Promise<string> {
logger.info(`Trying with local server... (${scopes})`);
return await window.withProgress<string>({
@@ -232,6 +238,9 @@ const allFlows: IFlow[] = [
['redirect_uri', redirectUri.toString(true)],
['scope', scopes],
]);
if (existingLogin) {
searchParams.append('login', existingLogin);
}
const loginUrl = baseUri.with({
path: '/login/oauth/authorize',

View File

@@ -11,7 +11,7 @@ import { PromiseAdapter, arrayEquals, promiseFromEvent } from './common/utils';
import { ExperimentationTelemetry } from './common/experimentationService';
import { Log } from './common/logger';
import { crypto } from './node/crypto';
import { TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
import { CANCELLATION_ERROR, TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
interface SessionData {
id: string;
@@ -296,13 +296,44 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
scopes: JSON.stringify(scopes),
});
const sessions = await this._sessionsPromise;
const scopeString = sortedScopes.join(' ');
const token = await this._githubServer.login(scopeString);
const existingLogin = sessions[0]?.account.label;
const token = await this._githubServer.login(scopeString, existingLogin);
const session = await this.tokenToSession(token, scopes);
this.afterSessionLoad(session);
const sessions = await this._sessionsPromise;
if (sessions.some(s => s.id !== session.id)) {
const otherAccountsIndexes = new Array<number>();
const otherAccountsLabels = new Set<string>();
for (let i = 0; i < sessions.length; i++) {
if (sessions[i].id !== session.id) {
otherAccountsIndexes.push(i);
otherAccountsLabels.add(sessions[i].account.label);
}
}
const proceed = vscode.l10n.t("Continue");
const labelstr = [...otherAccountsLabels].join(', ');
const result = await vscode.window.showInformationMessage(
vscode.l10n.t({
message: "You are logged into another account already ({0}).\n\nDo you want to log out of that account and log in to '{1}' instead?",
comment: ['{0} is a comma-separated list of account names. {1} is the account name to log into.'],
args: [labelstr, session.account.label]
}),
{ modal: true },
proceed
);
if (result !== proceed) {
throw new Error(CANCELLATION_ERROR);
}
// Remove other accounts
for (const i of otherAccountsIndexes) {
sessions.splice(i, 1);
}
}
const sessionIndex = sessions.findIndex(s => s.id === session.id || arrayEquals([...s.scopes].sort(), sortedScopes));
if (sessionIndex > -1) {
sessions.splice(sessionIndex, 1, session);

View File

@@ -11,19 +11,15 @@ import { isSupportedClient, isSupportedTarget } from './common/env';
import { crypto } from './node/crypto';
import { fetching } from './node/fetch';
import { ExtensionHost, GitHubTarget, getFlows } from './flows';
import { NETWORK_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
import { CANCELLATION_ERROR, NETWORK_ERROR, USER_CANCELLATION_ERROR } from './common/errors';
import { Config } from './config';
import { base64Encode } from './node/buffer';
// This is the error message that we throw if the login was cancelled for any reason. Extensions
// calling `getSession` can handle this error to know that the user cancelled the login.
const CANCELLATION_ERROR = 'Cancelled';
const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect';
const REDIRECT_URL_INSIDERS = 'https://insiders.vscode.dev/redirect';
export interface IGitHubServer {
login(scopes: string): Promise<string>;
login(scopes: string, existingLogin?: string): Promise<string>;
logout(session: vscode.AuthenticationSession): Promise<void>;
getUserInfo(token: string): Promise<{ id: string; accountName: string }>;
sendAdditionalTelemetryInfo(session: vscode.AuthenticationSession): Promise<void>;
@@ -91,7 +87,7 @@ export class GitHubServer implements IGitHubServer {
return this._isNoCorsEnvironment;
}
public async login(scopes: string): Promise<string> {
public async login(scopes: string, existingLogin?: string): Promise<string> {
this._logger.info(`Logging in for the following scopes: ${scopes}`);
// Used for showing a friendlier message to the user when the explicitly cancel a flow.
@@ -143,6 +139,7 @@ export class GitHubServer implements IGitHubServer {
uriHandler: this._uriHandler,
enterpriseUri: this._ghesUri,
redirectUri: vscode.Uri.parse(await this.getRedirectEndpoint()),
existingLogin
});
} catch (e) {
userCancelled = this.processLoginError(e);