mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-25 19:18:59 +01:00
refactor github auth to be a bit simpler. Remove PAT for GitHub auth since Settings Sync doesn't allow for it and add timeout so that GitHub Auth is not stuck.
This commit is contained in:
@@ -6,11 +6,11 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { Keychain } from './common/keychain';
|
||||
import { GitHubServer, uriHandler } from './githubServer';
|
||||
import Logger from './common/logger';
|
||||
import { GitHubEnterpriseServer, GitHubServer, IGitHubServer } from './githubServer';
|
||||
import { arrayEquals } from './common/utils';
|
||||
import { ExperimentationTelemetry } from './experimentationService';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
import { Log } from './common/logger';
|
||||
|
||||
interface SessionData {
|
||||
id: string;
|
||||
@@ -30,29 +30,31 @@ export enum AuthProviderType {
|
||||
|
||||
export class GitHubAuthenticationProvider implements vscode.AuthenticationProvider, vscode.Disposable {
|
||||
private _sessionChangeEmitter = new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
|
||||
private _githubServer: GitHubServer;
|
||||
private _logger = new Log(this.type);
|
||||
private _githubServer: IGitHubServer;
|
||||
private _telemetryReporter: ExperimentationTelemetry;
|
||||
|
||||
private _keychain: Keychain;
|
||||
private _keychain: Keychain = new Keychain(this.context, `${this.type}.auth`, this._logger);
|
||||
private _sessionsPromise: Promise<vscode.AuthenticationSession[]>;
|
||||
private _disposable: vscode.Disposable;
|
||||
|
||||
constructor(private context: vscode.ExtensionContext, private type: AuthProviderType) {
|
||||
constructor(private readonly context: vscode.ExtensionContext, private readonly type: AuthProviderType) {
|
||||
const { name, version, aiKey } = context.extension.packageJSON as { name: string, version: string, aiKey: string };
|
||||
this._telemetryReporter = new ExperimentationTelemetry(context, new TelemetryReporter(name, version, aiKey));
|
||||
|
||||
this._keychain = new Keychain(context, `${type}.auth`);
|
||||
this._githubServer = new GitHubServer(type, this._telemetryReporter);
|
||||
if (this.type === AuthProviderType.github) {
|
||||
this._githubServer = new GitHubServer(this._logger, this._telemetryReporter);
|
||||
} else {
|
||||
this._githubServer = new GitHubEnterpriseServer(this._logger, this._telemetryReporter);
|
||||
}
|
||||
|
||||
// Contains the current state of the sessions we have available.
|
||||
this._sessionsPromise = this.readSessions();
|
||||
|
||||
const friendlyName = this.type === AuthProviderType.github ? 'GitHub' : 'GitHub Enterprise';
|
||||
this._disposable = vscode.Disposable.from(
|
||||
this._telemetryReporter,
|
||||
this.type === AuthProviderType.github ? vscode.window.registerUriHandler(uriHandler) : { dispose() { } },
|
||||
vscode.commands.registerCommand(`${this.type}.provide-token`, () => this.manuallyProvideToken()),
|
||||
vscode.authentication.registerAuthenticationProvider(this.type, friendlyName, this, { supportsMultipleAccounts: false }),
|
||||
this._githubServer,
|
||||
vscode.authentication.registerAuthenticationProvider(type, this._githubServer.friendlyName, this, { supportsMultipleAccounts: false }),
|
||||
this.context.secrets.onDidChange(() => this.checkForUpdates())
|
||||
);
|
||||
}
|
||||
@@ -66,23 +68,18 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
}
|
||||
|
||||
async getSessions(scopes?: string[]): Promise<vscode.AuthenticationSession[]> {
|
||||
Logger.info(`Getting sessions for ${scopes?.join(',') || 'all scopes'}...`);
|
||||
this._logger.info(`Getting sessions for ${scopes?.join(',') || 'all scopes'}...`);
|
||||
const sessions = await this._sessionsPromise;
|
||||
const finalSessions = scopes
|
||||
? sessions.filter(session => arrayEquals([...session.scopes].sort(), scopes.sort()))
|
||||
: sessions;
|
||||
|
||||
Logger.info(`Got ${finalSessions.length} sessions for ${scopes?.join(',') || 'all scopes'}...`);
|
||||
this._logger.info(`Got ${finalSessions.length} sessions for ${scopes?.join(',') || 'all scopes'}...`);
|
||||
return finalSessions;
|
||||
}
|
||||
|
||||
private async afterTokenLoad(token: string): Promise<void> {
|
||||
if (this.type === AuthProviderType.github) {
|
||||
this._githubServer.checkIsEdu(token);
|
||||
}
|
||||
if (this.type === AuthProviderType.githubEnterprise) {
|
||||
this._githubServer.checkEnterpriseVersion(token);
|
||||
}
|
||||
this._githubServer.sendAdditionalTelemetryInfo(token);
|
||||
}
|
||||
|
||||
private async checkForUpdates() {
|
||||
@@ -97,7 +94,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
const matchesExisting = previousSessions.some(s => s.id === session.id);
|
||||
// Another window added a session to the keychain, add it to our state as well
|
||||
if (!matchesExisting) {
|
||||
Logger.info('Adding session found in keychain');
|
||||
this._logger.info('Adding session found in keychain');
|
||||
added.push(session);
|
||||
}
|
||||
});
|
||||
@@ -106,7 +103,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
const matchesExisting = storedSessions.some(s => s.id === session.id);
|
||||
// Another window has logged out, remove from our state
|
||||
if (!matchesExisting) {
|
||||
Logger.info('Removing session no longer found in keychain');
|
||||
this._logger.info('Removing session no longer found in keychain');
|
||||
removed.push(session);
|
||||
}
|
||||
});
|
||||
@@ -119,12 +116,12 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
private async readSessions(): Promise<vscode.AuthenticationSession[]> {
|
||||
let sessionData: SessionData[];
|
||||
try {
|
||||
Logger.info('Reading sessions from keychain...');
|
||||
this._logger.info('Reading sessions from keychain...');
|
||||
const storedSessions = await this._keychain.getToken() || await this._keychain.tryMigrate();
|
||||
if (!storedSessions) {
|
||||
return [];
|
||||
}
|
||||
Logger.info('Got stored sessions!');
|
||||
this._logger.info('Got stored sessions!');
|
||||
|
||||
try {
|
||||
sessionData = JSON.parse(storedSessions);
|
||||
@@ -133,7 +130,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
throw e;
|
||||
}
|
||||
} catch (e) {
|
||||
Logger.error(`Error reading token: ${e}`);
|
||||
this._logger.error(`Error reading token: ${e}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -142,7 +139,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
if (!session.account) {
|
||||
try {
|
||||
userInfo = await this._githubServer.getUserInfo(session.accessToken);
|
||||
Logger.info(`Verified session with the following scopes: ${session.scopes}`);
|
||||
this._logger.info(`Verified session with the following scopes: ${session.scopes}`);
|
||||
} catch (e) {
|
||||
// Remove sessions that return unauthorized response
|
||||
if (e.message === 'Unauthorized') {
|
||||
@@ -153,7 +150,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
|
||||
setTimeout(() => this.afterTokenLoad(session.accessToken), 1000);
|
||||
|
||||
Logger.trace(`Read the following session from the keychain with the following scopes: ${session.scopes}`);
|
||||
this._logger.trace(`Read the following session from the keychain with the following scopes: ${session.scopes}`);
|
||||
return {
|
||||
id: session.id,
|
||||
account: {
|
||||
@@ -172,7 +169,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
.map(p => (p as PromiseFulfilledResult<vscode.AuthenticationSession | undefined>).value)
|
||||
.filter(<T>(p?: T): p is T => Boolean(p));
|
||||
|
||||
Logger.info(`Got ${verifiedSessions.length} verified sessions.`);
|
||||
this._logger.info(`Got ${verifiedSessions.length} verified sessions.`);
|
||||
if (verifiedSessions.length !== sessionData.length) {
|
||||
await this.storeSessions(verifiedSessions);
|
||||
}
|
||||
@@ -181,10 +178,10 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
}
|
||||
|
||||
private async storeSessions(sessions: vscode.AuthenticationSession[]): Promise<void> {
|
||||
Logger.info(`Storing ${sessions.length} sessions...`);
|
||||
this._logger.info(`Storing ${sessions.length} sessions...`);
|
||||
this._sessionsPromise = Promise.resolve(sessions);
|
||||
await this._keychain.setToken(JSON.stringify(sessions));
|
||||
Logger.info(`Stored ${sessions.length} sessions!`);
|
||||
this._logger.info(`Stored ${sessions.length} sessions!`);
|
||||
}
|
||||
|
||||
public async createSession(scopes: string[]): Promise<vscode.AuthenticationSession> {
|
||||
@@ -213,7 +210,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
|
||||
this._sessionChangeEmitter.fire({ added: [session], removed: [], changed: [] });
|
||||
|
||||
Logger.info('Login success!');
|
||||
this._logger.info('Login success!');
|
||||
|
||||
return session;
|
||||
} catch (e) {
|
||||
@@ -232,15 +229,11 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
this._telemetryReporter?.sendTelemetryEvent('loginFailed');
|
||||
|
||||
vscode.window.showErrorMessage(`Sign in failed: ${e}`);
|
||||
Logger.error(e);
|
||||
this._logger.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public async manuallyProvideToken(): Promise<void> {
|
||||
this._githubServer.manuallyProvideToken();
|
||||
}
|
||||
|
||||
private async tokenToSession(token: string, scopes: string[]): Promise<vscode.AuthenticationSession> {
|
||||
const userInfo = await this._githubServer.getUserInfo(token);
|
||||
return {
|
||||
@@ -258,7 +251,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
*/
|
||||
this._telemetryReporter?.sendTelemetryEvent('logout');
|
||||
|
||||
Logger.info(`Logging out of ${id}`);
|
||||
this._logger.info(`Logging out of ${id}`);
|
||||
|
||||
const sessions = await this._sessionsPromise;
|
||||
const sessionIndex = sessions.findIndex(session => session.id === id);
|
||||
@@ -270,7 +263,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
|
||||
this._sessionChangeEmitter.fire({ added: [], removed: [session], changed: [] });
|
||||
} else {
|
||||
Logger.error('Session not found');
|
||||
this._logger.error('Session not found');
|
||||
}
|
||||
} catch (e) {
|
||||
/* __GDPR__
|
||||
@@ -279,7 +272,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid
|
||||
this._telemetryReporter?.sendTelemetryEvent('logoutFailed');
|
||||
|
||||
vscode.window.showErrorMessage(`Sign out failed: ${e}`);
|
||||
Logger.error(e);
|
||||
this._logger.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user