mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Organize Errors in GitHub Auth and make sure no double prompting happens (#180734)
* Organize Errors in GitHub Auth and make sure no double prompting happens This mostly just moves some strings into variables... but this also fixes the GH Auth side of https://github.com/microsoft/vscode/issues/180697 so you should only be asked once if you want to try a different way to log in. * add comments
This commit is contained in:
parent
af8b51ed1e
commit
1714f71c41
|
@ -16,6 +16,13 @@ import { fetching } from './node/fetch';
|
||||||
|
|
||||||
const CLIENT_ID = '01ab8ac9400c4e429b23';
|
const CLIENT_ID = '01ab8ac9400c4e429b23';
|
||||||
const GITHUB_TOKEN_URL = 'https://vscode.dev/codeExchangeProxyEndpoints/github/login/oauth/access_token';
|
const GITHUB_TOKEN_URL = 'https://vscode.dev/codeExchangeProxyEndpoints/github/login/oauth/access_token';
|
||||||
|
|
||||||
|
// 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';
|
||||||
|
// These error messages are internal and should not be shown to the user in any way.
|
||||||
|
const TIMED_OUT_ERROR = 'Timed out';
|
||||||
|
const USER_CANCELLATION_ERROR = 'User Cancelled';
|
||||||
const NETWORK_ERROR = 'network error';
|
const NETWORK_ERROR = 'network error';
|
||||||
|
|
||||||
const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect';
|
const REDIRECT_URL_STABLE = 'https://vscode.dev/redirect';
|
||||||
|
@ -132,7 +139,7 @@ export class GitHubServer implements IGitHubServer {
|
||||||
: vscode.l10n.t('You have not yet finished authorizing this extension to use GitHub. Would you like to keep trying?');
|
: vscode.l10n.t('You have not yet finished authorizing this extension to use GitHub. Would you like to keep trying?');
|
||||||
const result = await vscode.window.showWarningMessage(message, yes, no);
|
const result = await vscode.window.showWarningMessage(message, yes, no);
|
||||||
if (result !== yes) {
|
if (result !== yes) {
|
||||||
throw new Error('Cancelled');
|
throw new Error(CANCELLATION_ERROR);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,7 +153,7 @@ export class GitHubServer implements IGitHubServer {
|
||||||
return await this.doLoginWithoutLocalServer(scopes, nonce, callbackUri);
|
return await this.doLoginWithoutLocalServer(scopes, nonce, callbackUri);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._logger.error(e);
|
this._logger.error(e);
|
||||||
userCancelled = e.message ?? e === 'User Cancelled';
|
userCancelled = e.message ?? e === USER_CANCELLATION_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,8 +170,7 @@ export class GitHubServer implements IGitHubServer {
|
||||||
await promptToContinue();
|
await promptToContinue();
|
||||||
return await this.doLoginWithLocalServer(scopes);
|
return await this.doLoginWithLocalServer(scopes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._logger.error(e);
|
userCancelled = this.processLoginError(e);
|
||||||
userCancelled = e.message ?? e === 'User Cancelled';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,8 +180,7 @@ export class GitHubServer implements IGitHubServer {
|
||||||
await promptToContinue();
|
await promptToContinue();
|
||||||
return await this.doLoginDeviceCodeFlow(scopes);
|
return await this.doLoginDeviceCodeFlow(scopes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._logger.error(e);
|
userCancelled = this.processLoginError(e);
|
||||||
userCancelled = e.message ?? e === 'User Cancelled';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,12 +191,11 @@ export class GitHubServer implements IGitHubServer {
|
||||||
await promptToContinue();
|
await promptToContinue();
|
||||||
return await this.doLoginWithPat(scopes);
|
return await this.doLoginWithPat(scopes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._logger.error(e);
|
userCancelled = this.processLoginError(e);
|
||||||
userCancelled = e.message ?? e === 'User Cancelled';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(userCancelled ? 'Cancelled' : 'No auth flow succeeded.');
|
throw new Error(userCancelled ? CANCELLATION_ERROR : 'No auth flow succeeded.');
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doLoginWithoutLocalServer(scopes: string, nonce: string, callbackUri: vscode.Uri): Promise<string> {
|
private async doLoginWithoutLocalServer(scopes: string, nonce: string, callbackUri: vscode.Uri): Promise<string> {
|
||||||
|
@ -232,8 +236,8 @@ export class GitHubServer implements IGitHubServer {
|
||||||
try {
|
try {
|
||||||
return await Promise.race([
|
return await Promise.race([
|
||||||
codeExchangePromise.promise,
|
codeExchangePromise.promise,
|
||||||
new Promise<string>((_, reject) => setTimeout(() => reject('Timed out'), 300_000)), // 5min timeout
|
new Promise<string>((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout
|
||||||
promiseFromEvent<any, any>(token.onCancellationRequested, (_, __, reject) => { reject('User Cancelled'); }).promise
|
promiseFromEvent<any, any>(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise
|
||||||
]);
|
]);
|
||||||
} finally {
|
} finally {
|
||||||
this._pendingNonces.delete(scopes);
|
this._pendingNonces.delete(scopes);
|
||||||
|
@ -273,8 +277,8 @@ export class GitHubServer implements IGitHubServer {
|
||||||
vscode.env.openExternal(vscode.Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`));
|
vscode.env.openExternal(vscode.Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`));
|
||||||
const { code } = await Promise.race([
|
const { code } = await Promise.race([
|
||||||
server.waitForOAuthResponse(),
|
server.waitForOAuthResponse(),
|
||||||
new Promise<any>((_, reject) => setTimeout(() => reject('Timed out'), 300_000)), // 5min timeout
|
new Promise<any>((_, reject) => setTimeout(() => reject(TIMED_OUT_ERROR), 300_000)), // 5min timeout
|
||||||
promiseFromEvent<any, any>(token.onCancellationRequested, (_, __, reject) => { reject('User Cancelled'); }).promise
|
promiseFromEvent<any, any>(token.onCancellationRequested, (_, __, reject) => { reject(USER_CANCELLATION_ERROR); }).promise
|
||||||
]);
|
]);
|
||||||
codeToExchange = code;
|
codeToExchange = code;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -317,7 +321,7 @@ export class GitHubServer implements IGitHubServer {
|
||||||
}, button);
|
}, button);
|
||||||
|
|
||||||
if (modalResult !== button) {
|
if (modalResult !== button) {
|
||||||
throw new Error('User Cancelled');
|
throw new Error(USER_CANCELLATION_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
await vscode.env.clipboard.writeText(json.user_code);
|
await vscode.env.clipboard.writeText(json.user_code);
|
||||||
|
@ -340,14 +344,14 @@ export class GitHubServer implements IGitHubServer {
|
||||||
}, button);
|
}, button);
|
||||||
|
|
||||||
if (modalResult !== button) {
|
if (modalResult !== button) {
|
||||||
throw new Error('User Cancelled');
|
throw new Error(USER_CANCELLATION_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
const description = `${vscode.env.appName} (${scopes})`;
|
const description = `${vscode.env.appName} (${scopes})`;
|
||||||
const uriToOpen = await vscode.env.asExternalUri(this.baseUri.with({ path: '/settings/tokens/new', query: `description=${description}&scopes=${scopes.split(' ').join(',')}` }));
|
const uriToOpen = await vscode.env.asExternalUri(this.baseUri.with({ path: '/settings/tokens/new', query: `description=${description}&scopes=${scopes.split(' ').join(',')}` }));
|
||||||
await vscode.env.openExternal(uriToOpen);
|
await vscode.env.openExternal(uriToOpen);
|
||||||
const token = await vscode.window.showInputBox({ placeHolder: `ghp_1a2b3c4...`, prompt: `GitHub Personal Access Token - ${scopes}`, ignoreFocusOut: true });
|
const token = await vscode.window.showInputBox({ placeHolder: `ghp_1a2b3c4...`, prompt: `GitHub Personal Access Token - ${scopes}`, ignoreFocusOut: true });
|
||||||
if (!token) { throw new Error('User Cancelled'); }
|
if (!token) { throw new Error(USER_CANCELLATION_ERROR); }
|
||||||
|
|
||||||
const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user']
|
const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user']
|
||||||
const scopesList = scopes.split(' '); // Example: 'read:user repo user:email'
|
const scopesList = scopes.split(' '); // Example: 'read:user repo user:email'
|
||||||
|
@ -392,7 +396,7 @@ export class GitHubServer implements IGitHubServer {
|
||||||
for (let i = 0; i < attempts; i++) {
|
for (let i = 0; i < attempts; i++) {
|
||||||
await new Promise(resolve => setTimeout(resolve, json.interval * 1000));
|
await new Promise(resolve => setTimeout(resolve, json.interval * 1000));
|
||||||
if (token.isCancellationRequested) {
|
if (token.isCancellationRequested) {
|
||||||
throw new Error('User Cancelled');
|
throw new Error(USER_CANCELLATION_ERROR);
|
||||||
}
|
}
|
||||||
let accessTokenResult;
|
let accessTokenResult;
|
||||||
try {
|
try {
|
||||||
|
@ -423,7 +427,7 @@ export class GitHubServer implements IGitHubServer {
|
||||||
return accessTokenJson.access_token;
|
return accessTokenJson.access_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Cancelled');
|
throw new Error(TIMED_OUT_ERROR);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,4 +645,12 @@ export class GitHubServer implements IGitHubServer {
|
||||||
// No-op
|
// No-op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private processLoginError(error: Error): boolean {
|
||||||
|
if (error.message === CANCELLATION_ERROR) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
this._logger.error(error.message ?? error);
|
||||||
|
return error.message === USER_CANCELLATION_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue