mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-25 19:18:59 +01:00
Adopt getAccounts API in GitHub Authentication (#221224)
This allows the GitHub Auth provider to take in a account per this proposal:
417dddb83f/src/vscode-dts/vscode.proposed.authGetSessions.d.ts (L35-L69)
Additionally, this makes an additional small change that allows `clearSessionPreference` to work in single-account auth providers.
This commit is contained in:
committed by
GitHub
parent
6c907814bf
commit
1b24381b5c
@@ -173,6 +173,8 @@ const allFlows: IFlow[] = [
|
||||
]);
|
||||
if (existingLogin) {
|
||||
searchParams.append('login', existingLogin);
|
||||
} else {
|
||||
searchParams.append('prompt', 'select_account');
|
||||
}
|
||||
|
||||
// The extra toString, parse is apparently needed for env.openExternal
|
||||
@@ -240,6 +242,8 @@ const allFlows: IFlow[] = [
|
||||
]);
|
||||
if (existingLogin) {
|
||||
searchParams.append('login', existingLogin);
|
||||
} else {
|
||||
searchParams.append('prompt', 'select_account');
|
||||
}
|
||||
|
||||
const loginUrl = baseUri.with({
|
||||
|
||||
@@ -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;
|
||||
@@ -148,14 +148,17 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
return this._sessionChangeEmitter.event;
|
||||
}
|
||||
|
||||
async getSessions(scopes?: string[]): Promise<vscode.AuthenticationSession[]> {
|
||||
async getSessions(scopes: string[] | undefined, options?: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession[]> {
|
||||
// For GitHub scope list, order doesn't matter so we immediately sort the scopes
|
||||
const sortedScopes = scopes?.sort() || [];
|
||||
this._logger.info(`Getting sessions for ${sortedScopes.length ? sortedScopes.join(',') : 'all scopes'}...`);
|
||||
const sessions = await this._sessionsPromise;
|
||||
const finalSessions = sortedScopes.length
|
||||
? sessions.filter(session => arrayEquals([...session.scopes].sort(), sortedScopes))
|
||||
const accountFilteredSessions = options?.account
|
||||
? sessions.filter(session => session.account.label === options.account?.label)
|
||||
: sessions;
|
||||
const finalSessions = sortedScopes.length
|
||||
? accountFilteredSessions.filter(session => arrayEquals([...session.scopes].sort(), sortedScopes))
|
||||
: accountFilteredSessions;
|
||||
|
||||
this._logger.info(`Got ${finalSessions.length} sessions for ${sortedScopes?.join(',') ?? 'all scopes'}...`);
|
||||
return finalSessions;
|
||||
@@ -279,7 +282,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
this._logger.info(`Stored ${sessions.length} sessions!`);
|
||||
}
|
||||
|
||||
public async createSession(scopes: string[]): Promise<vscode.AuthenticationSession> {
|
||||
public async createSession(scopes: string[], options?: vscode.AuthenticationProviderSessionOptions): Promise<vscode.AuthenticationSession> {
|
||||
try {
|
||||
// For GitHub scope list, order doesn't matter so we use a sorted scope to determine
|
||||
// if we've got a session already.
|
||||
@@ -298,11 +301,59 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
|
||||
const sessions = await this._sessionsPromise;
|
||||
|
||||
const accounts = new Set(sessions.map(session => session.account.label));
|
||||
const existingLogin = accounts.size <= 1 ? sessions[0]?.account.label : await vscode.window.showQuickPick([...accounts], { placeHolder: 'Choose an account that you would like to log in to' });
|
||||
let forcedLogin = options?.account?.label;
|
||||
let backupLogin: string | undefined;
|
||||
if (!forcedLogin) {
|
||||
const accounts = new Set(sessions.map(session => session.account.label));
|
||||
this._logger.info(`Found ${accounts.size} accounts.`);
|
||||
// This helps us tell GitHub that we're already logged in to an account/accounts
|
||||
// and should probably use it. The user _can_ sign in to a different account
|
||||
// if they want to, in the browser, but this is a good default for the happy path.
|
||||
if (accounts.size > 1) {
|
||||
// If there are multiple accounts, we should prompt the user to choose one.
|
||||
const newAccount = vscode.l10n.t('New account...');
|
||||
const accountChoiceResult = await vscode.window.showQuickPick(
|
||||
[...accounts, newAccount],
|
||||
{ placeHolder: vscode.l10n.t('Choose an account that you would like to log in to') }
|
||||
);
|
||||
forcedLogin = accountChoiceResult === newAccount ? undefined : accountChoiceResult;
|
||||
} else {
|
||||
// If there is only one account, we can use that to seed the login, but
|
||||
// we don't want to force the user to use it.
|
||||
backupLogin = sessions[0]?.account.label;
|
||||
}
|
||||
}
|
||||
this._logger.info(`Logging in with '${forcedLogin ? forcedLogin : 'any'}' account...`);
|
||||
|
||||
const scopeString = sortedScopes.join(' ');
|
||||
const token = await this._githubServer.login(scopeString, existingLogin);
|
||||
const token = await this._githubServer.login(scopeString, forcedLogin ?? backupLogin);
|
||||
const session = await this.tokenToSession(token, scopes);
|
||||
|
||||
// If an account was specified, we should ensure that the token we got back is for that account.
|
||||
if (forcedLogin) {
|
||||
if (session.account.label !== forcedLogin) {
|
||||
const keepNewAccount = vscode.l10n.t('Keep {0}', session.account.label);
|
||||
const tryAgain = vscode.l10n.t('Login with {0}', forcedLogin);
|
||||
const result = await vscode.window.showWarningMessage(
|
||||
vscode.l10n.t('Incorrect account detected'),
|
||||
{ modal: true, detail: vscode.l10n.t('The chosen account, {0}, does not match the requested account, {1}.', session.account.label, forcedLogin) },
|
||||
keepNewAccount,
|
||||
tryAgain
|
||||
);
|
||||
if (result === tryAgain) {
|
||||
return await this.createSession(scopes, {
|
||||
...options,
|
||||
// The id doesn't matter here, we just need to pass the label through
|
||||
account: { id: forcedLogin, label: forcedLogin }
|
||||
});
|
||||
}
|
||||
// Cancelled result
|
||||
if (!result) {
|
||||
throw new Error(CANCELLATION_ERROR);
|
||||
}
|
||||
// Keep result continues on
|
||||
}
|
||||
}
|
||||
this.afterSessionLoad(session);
|
||||
|
||||
const sessionIndex = sessions.findIndex(s => s.id === session.id || arrayEquals([...s.scopes].sort(), sortedScopes));
|
||||
|
||||
Reference in New Issue
Block a user