Use auth token when connecting to existing server from Electron

Summary:
All clients need to provide an authentication token before connecting. Electron app is no different.

This change adds the authentication token whether we are connecting over UDS or TCP.

Before this change, if Flipper server was already running, launching the Electron app would look similar to this:

{F1016961594}

Reviewed By: antonk52

Differential Revision: D46418758

fbshipit-source-id: f00ffe675df78403d5921250e3e9ed9331a55bde
This commit is contained in:
Lorenzo Blasa
2023-06-05 05:26:10 -07:00
committed by Facebook GitHub Bot
parent 90240f4186
commit 98f376ec69
3 changed files with 75 additions and 47 deletions

View File

@@ -19,9 +19,12 @@ import {
FlipperServerState,
} from 'flipper-server-client';
import {
checkPortInUse,
FlipperServerImpl,
getAuthToken,
getEnvironmentInfo,
getGatekeepers,
hasAuthToken,
loadLauncherSettings,
loadProcessConfig,
loadSettings,
@@ -77,7 +80,7 @@ async function getKeytarModule(staticPath: string): Promise<KeytarModule> {
return keytar;
}
async function getExternalServer(url: string) {
async function getExternalServer(url: URL) {
const options = {
WebSocket: class WSWithUnixDomainSocketSupport extends WS {
constructor(url: string, protocols: string | string[]) {
@@ -87,11 +90,12 @@ async function getExternalServer(url: string) {
}
},
};
const socket = new ReconnectingWebSocket(url, [], options);
const socket = new ReconnectingWebSocket(url.toString(), [], options);
const server = await createFlipperServerWithSocket(
socket as WebSocket,
(_state: FlipperServerState) => {},
);
return server;
}
@@ -117,19 +121,51 @@ async function getFlipperServer(
const settings = await loadSettings();
const socketPath = await makeSocketPath();
let externalServerConnectionURL = `ws+unix://${socketPath}`;
const port = 52342;
/**
* Only attempt to use the auth token if one is available. Otherwise,
* trying to get the auth token will try to generate one if it does not exist.
* At this state, it would be impossible to generate it as our certificates
* may not be available yet.
*/
let token: string | undefined;
if (await hasAuthToken()) {
token = await getAuthToken();
}
// check first with the actual TCP socket
const searchParams = new URLSearchParams(token ? {token} : {});
const TCPconnectionURL = new URL(`ws://localhost:${port}?${searchParams}`);
const UDSconnectionURL = new URL(`ws+unix://${socketPath}`);
// On Windows this is going to return false at all times as we do not use domain sockets there.
let serverRunning = await checkSocketInUse(socketPath);
if (serverRunning) {
console.info(
'flipper-server: currently running/listening, attempt to shutdown',
);
const server = await getExternalServer(externalServerConnectionURL);
/**
* Attempt to shutdown a running instance of Flipper server.
* @param url The URL used for connection.
*/
async function shutdown(url: URL) {
console.info('[flipper-server] Attempt to shutdown.');
const server = await getExternalServer(url);
await server.exec('shutdown').catch(() => {
/** shutdown will ultimately make this request fail, ignore error. */
console.info('[flipper-server] Shutdown may have succeeded');
});
serverRunning = false;
}
/**
* In this case, order matters. First, check if the TCP port is in use. If so,
* then shut down the TCP socket. If not, then try the UDS socket.
*
* UDS doesn't accept arguments in the query string, this effectively creates a different
* socket path which then doesn't match the one used by the server.
*/
if (await checkPortInUse(port)) {
console.warn(`[flipper-server] TCP port ${port} is already in use.`);
await shutdown(TCPconnectionURL);
} else if (await checkSocketInUse(socketPath)) {
console.warn(`[flipper-server] UDS socket is already in use.`);
await shutdown(UDSconnectionURL);
}
const getEmbeddedServer = async () => {
@@ -158,15 +194,9 @@ async function getFlipperServer(
return server;
};
// Failed to start Flipper desktop: Error: flipper-server disconnected
if (serverUsageEnabled && (!settings.server || settings.server.enabled)) {
if (!serverRunning) {
console.info('flipper-server: not running/listening, start');
const port = 52342;
if (os.platform() === 'win32') {
externalServerConnectionURL = `ws://localhost:${port}`;
}
if (serverUsageEnabled && (!settings.server || settings.server.enabled)) {
console.info('flipper-server: not running/listening, start');
const {readyForIncomingConnections} = await startServer({
staticPath,
@@ -187,11 +217,9 @@ async function getFlipperServer(
const companionEnv = await initCompanionEnv(server);
await server.connect();
await readyForIncomingConnections(server, companionEnv);
}
return getExternalServer(externalServerConnectionURL);
return getExternalServer(UDSconnectionURL);
}
return getEmbeddedServer();
}

View File

@@ -22,4 +22,4 @@ export {isFBBuild} from './fb-stubs/constants';
export {WEBSOCKET_MAX_MESSAGE_SIZE} from './comms/ServerWebSocket';
export {getAuthToken} from './utils/certificateUtils';
export {getAuthToken, hasAuthToken} from './utils/certificateUtils';

View File

@@ -266,10 +266,6 @@ const writeToTempFile = async (content: string): Promise<string> => {
return path;
};
const tokenFilename = 'auth.token';
const getTokenPath = (config: FlipperServerConfig): string => {
return getFilePath(tokenFilename);
};
const manifestFilename = 'manifest.json';
const getManifestPath = (config: FlipperServerConfig): string => {
return path.resolve(config.paths.staticPath, manifestFilename);
@@ -315,8 +311,8 @@ export const generateAuthToken = async () => {
return token;
};
export const getAuthToken = async () => {
if (!(await fs.pathExists(serverAuthToken))) {
export const getAuthToken = async (): Promise<string> => {
if (!(await hasAuthToken())) {
return generateAuthToken();
}
@@ -324,6 +320,10 @@ export const getAuthToken = async () => {
return token.toString();
};
export const hasAuthToken = async (): Promise<boolean> => {
return fs.pathExists(serverAuthToken);
};
export const validateAuthToken = (token: string) => {
if (!serverConfig) {
throw new Error(