mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
Remove unused LoopbackAuthServer from microsoft-authentication (#295254)
The LoopbackAuthServer class in extensions/microsoft-authentication is dead code with no references. The active loopback servers live in extensions/github-authentication and src/vs/workbench/api/node.
This commit is contained in:
committed by
GitHub
parent
b449680673
commit
80ee2d8f15
@@ -1,207 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as http from 'http';
|
||||
import { URL } from 'url';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
function sendFile(res: http.ServerResponse, filepath: string) {
|
||||
fs.readFile(filepath, (err, body) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
} else {
|
||||
res.writeHead(200, {
|
||||
'content-length': body.length,
|
||||
});
|
||||
res.end(body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface IOAuthResult {
|
||||
code: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
interface ILoopbackServer {
|
||||
/**
|
||||
* If undefined, the server is not started yet.
|
||||
*/
|
||||
port: number | undefined;
|
||||
|
||||
/**
|
||||
* The nonce used
|
||||
*/
|
||||
nonce: string;
|
||||
|
||||
/**
|
||||
* The state parameter used in the OAuth flow.
|
||||
*/
|
||||
state: string | undefined;
|
||||
|
||||
/**
|
||||
* Starts the server.
|
||||
* @returns The port to listen on.
|
||||
* @throws If the server fails to start.
|
||||
* @throws If the server is already started.
|
||||
*/
|
||||
start(): Promise<number>;
|
||||
/**
|
||||
* Stops the server.
|
||||
* @throws If the server is not started.
|
||||
* @throws If the server fails to stop.
|
||||
*/
|
||||
stop(): Promise<void>;
|
||||
/**
|
||||
* Returns a promise that resolves to the result of the OAuth flow.
|
||||
*/
|
||||
waitForOAuthResponse(): Promise<IOAuthResult>;
|
||||
}
|
||||
|
||||
export class LoopbackAuthServer implements ILoopbackServer {
|
||||
private readonly _server: http.Server;
|
||||
private readonly _resultPromise: Promise<IOAuthResult>;
|
||||
private _startingRedirect: URL;
|
||||
|
||||
public nonce = randomBytes(16).toString('base64');
|
||||
public port: number | undefined;
|
||||
|
||||
public set state(state: string | undefined) {
|
||||
if (state) {
|
||||
this._startingRedirect.searchParams.set('state', state);
|
||||
} else {
|
||||
this._startingRedirect.searchParams.delete('state');
|
||||
}
|
||||
}
|
||||
public get state(): string | undefined {
|
||||
return this._startingRedirect.searchParams.get('state') ?? undefined;
|
||||
}
|
||||
|
||||
constructor(serveRoot: string, startingRedirect: string) {
|
||||
if (!serveRoot) {
|
||||
throw new Error('serveRoot must be defined');
|
||||
}
|
||||
if (!startingRedirect) {
|
||||
throw new Error('startingRedirect must be defined');
|
||||
}
|
||||
this._startingRedirect = new URL(startingRedirect);
|
||||
let deferred: { resolve: (result: IOAuthResult) => void; reject: (reason: any) => void };
|
||||
this._resultPromise = new Promise<IOAuthResult>((resolve, reject) => deferred = { resolve, reject });
|
||||
|
||||
this._server = http.createServer((req, res) => {
|
||||
const reqUrl = new URL(req.url!, `http://${req.headers.host}`);
|
||||
switch (reqUrl.pathname) {
|
||||
case '/signin': {
|
||||
const receivedNonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+');
|
||||
if (receivedNonce !== this.nonce) {
|
||||
res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` });
|
||||
res.end();
|
||||
}
|
||||
res.writeHead(302, { location: this._startingRedirect.toString() });
|
||||
res.end();
|
||||
break;
|
||||
}
|
||||
case '/callback': {
|
||||
const code = reqUrl.searchParams.get('code') ?? undefined;
|
||||
const state = reqUrl.searchParams.get('state') ?? undefined;
|
||||
const nonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+');
|
||||
const error = reqUrl.searchParams.get('error') ?? undefined;
|
||||
if (error) {
|
||||
res.writeHead(302, { location: `/?error=${reqUrl.searchParams.get('error_description')}` });
|
||||
res.end();
|
||||
deferred.reject(new Error(error));
|
||||
break;
|
||||
}
|
||||
if (!code || !state || !nonce) {
|
||||
res.writeHead(400);
|
||||
res.end();
|
||||
break;
|
||||
}
|
||||
if (this.state !== state) {
|
||||
res.writeHead(302, { location: `/?error=${encodeURIComponent('State does not match.')}` });
|
||||
res.end();
|
||||
deferred.reject(new Error('State does not match.'));
|
||||
break;
|
||||
}
|
||||
if (this.nonce !== nonce) {
|
||||
res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` });
|
||||
res.end();
|
||||
deferred.reject(new Error('Nonce does not match.'));
|
||||
break;
|
||||
}
|
||||
deferred.resolve({ code, state });
|
||||
res.writeHead(302, { location: '/' });
|
||||
res.end();
|
||||
break;
|
||||
}
|
||||
// Serve the static files
|
||||
case '/':
|
||||
sendFile(res, path.join(serveRoot, 'index.html'));
|
||||
break;
|
||||
default:
|
||||
// substring to get rid of leading '/'
|
||||
sendFile(res, path.join(serveRoot, reqUrl.pathname.substring(1)));
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public start(): Promise<number> {
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
if (this._server.listening) {
|
||||
throw new Error('Server is already started');
|
||||
}
|
||||
const portTimeout = setTimeout(() => {
|
||||
reject(new Error('Timeout waiting for port'));
|
||||
}, 5000);
|
||||
this._server.on('listening', () => {
|
||||
const address = this._server.address();
|
||||
if (typeof address === 'string') {
|
||||
this.port = parseInt(address);
|
||||
} else if (address instanceof Object) {
|
||||
this.port = address.port;
|
||||
} else {
|
||||
throw new Error('Unable to determine port');
|
||||
}
|
||||
|
||||
clearTimeout(portTimeout);
|
||||
|
||||
// set state which will be used to redirect back to vscode
|
||||
this.state = `http://127.0.0.1:${this.port}/callback?nonce=${encodeURIComponent(this.nonce)}`;
|
||||
|
||||
resolve(this.port);
|
||||
});
|
||||
this._server.on('error', err => {
|
||||
reject(new Error(`Error listening to server: ${err}`));
|
||||
});
|
||||
this._server.on('close', () => {
|
||||
reject(new Error('Closed'));
|
||||
});
|
||||
this._server.listen(0, '127.0.0.1');
|
||||
});
|
||||
}
|
||||
|
||||
public stop(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (!this._server.listening) {
|
||||
throw new Error('Server is not started');
|
||||
}
|
||||
this._server.close((err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public waitForOAuthResponse(): Promise<IOAuthResult> {
|
||||
return this._resultPromise;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user