mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-18 18:03:19 +01:00
Re-work auth flows into separate file (#186939)
The flows are crucial to the extension and I wanted to be able to declare which can be used when in a declarative way. This reworks the flows into that model. Additionally, it pulls in the Client ID and secret from a config which will allow us to not rely on the vscode.dev proxy on Desktop (because we can make a distro change that includes the secret... which isn't a real secret, says GitHub)... unfortunately we still need to rely on it for Web due to CORS but we're in a position where it will be easy to rip the proxy out when GH supports it.
This commit is contained in:
committed by
GitHub
parent
923091ec96
commit
013132a5cd
@@ -7,10 +7,11 @@ import * as vscode from 'vscode';
|
||||
import TelemetryReporter from '@vscode/extension-telemetry';
|
||||
import { Keychain } from './common/keychain';
|
||||
import { GitHubServer, IGitHubServer } from './githubServer';
|
||||
import { arrayEquals } from './common/utils';
|
||||
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';
|
||||
|
||||
interface SessionData {
|
||||
id: string;
|
||||
@@ -29,9 +30,63 @@ export enum AuthProviderType {
|
||||
}
|
||||
|
||||
export class UriEventHandler extends vscode.EventEmitter<vscode.Uri> implements vscode.UriHandler {
|
||||
private readonly _pendingNonces = new Map<string, string[]>();
|
||||
private readonly _codeExchangePromises = new Map<string, { promise: Promise<string>; cancel: vscode.EventEmitter<void> }>();
|
||||
|
||||
public handleUri(uri: vscode.Uri) {
|
||||
this.fire(uri);
|
||||
}
|
||||
|
||||
public async waitForCode(logger: Log, scopes: string, nonce: string, token: vscode.CancellationToken) {
|
||||
const existingNonces = this._pendingNonces.get(scopes) || [];
|
||||
this._pendingNonces.set(scopes, [...existingNonces, nonce]);
|
||||
|
||||
let codeExchangePromise = this._codeExchangePromises.get(scopes);
|
||||
if (!codeExchangePromise) {
|
||||
codeExchangePromise = promiseFromEvent(this.event, this.handleEvent(logger, scopes));
|
||||
this._codeExchangePromises.set(scopes, codeExchangePromise);
|
||||
}
|
||||
|
||||
try {
|
||||
return await Promise.race([
|
||||
codeExchangePromise.promise,
|
||||
new Promise<string>((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout
|
||||
promiseFromEvent<void, string>(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise
|
||||
]);
|
||||
} finally {
|
||||
this._pendingNonces.delete(scopes);
|
||||
codeExchangePromise?.cancel.fire();
|
||||
this._codeExchangePromises.delete(scopes);
|
||||
}
|
||||
}
|
||||
|
||||
private handleEvent: (logger: Log, scopes: string) => PromiseAdapter<vscode.Uri, string> =
|
||||
(logger: Log, 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'
|
||||
logger.info('Nonce not found in accepted nonces. Skipping this execution...');
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(code);
|
||||
};
|
||||
}
|
||||
|
||||
export class GitHubAuthenticationProvider implements vscode.AuthenticationProvider, vscode.Disposable {
|
||||
|
||||
Reference in New Issue
Block a user