From 98f376ec69f063258f686385d3881616c4990d22 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Mon, 5 Jun 2023 05:26:10 -0700 Subject: [PATCH] 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 --- desktop/app/src/init.tsx | 108 +++++++++++------- desktop/flipper-server-core/src/index.tsx | 2 +- .../src/utils/certificateUtils.tsx | 12 +- 3 files changed, 75 insertions(+), 47 deletions(-) diff --git a/desktop/app/src/init.tsx b/desktop/app/src/init.tsx index 7dba9d674..5d268a825 100644 --- a/desktop/app/src/init.tsx +++ b/desktop/app/src/init.tsx @@ -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 { 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,40 +194,32 @@ 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'); + console.info('flipper-server: not running/listening, start'); - const port = 52342; - if (os.platform() === 'win32') { - externalServerConnectionURL = `ws://localhost:${port}`; - } + const {readyForIncomingConnections} = await startServer({ + staticPath, + entry: 'index.web.dev.html', + tcp: false, + port, + }); - const {readyForIncomingConnections} = await startServer({ - staticPath, - entry: 'index.web.dev.html', - tcp: false, - port, - }); + const server = await startFlipperServer( + appPath, + staticPath, + '', + false, + keytar, + 'embedded', + environmentInfo, + ); - const server = await startFlipperServer( - appPath, - staticPath, - '', - false, - keytar, - 'embedded', - environmentInfo, - ); + const companionEnv = await initCompanionEnv(server); + await server.connect(); + await readyForIncomingConnections(server, companionEnv); - const companionEnv = await initCompanionEnv(server); - await server.connect(); - - await readyForIncomingConnections(server, companionEnv); - } - - return getExternalServer(externalServerConnectionURL); + return getExternalServer(UDSconnectionURL); } return getEmbeddedServer(); } diff --git a/desktop/flipper-server-core/src/index.tsx b/desktop/flipper-server-core/src/index.tsx index a9cbf3e45..627d651f4 100644 --- a/desktop/flipper-server-core/src/index.tsx +++ b/desktop/flipper-server-core/src/index.tsx @@ -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'; diff --git a/desktop/flipper-server-core/src/utils/certificateUtils.tsx b/desktop/flipper-server-core/src/utils/certificateUtils.tsx index d6ba92c74..4e06b9329 100644 --- a/desktop/flipper-server-core/src/utils/certificateUtils.tsx +++ b/desktop/flipper-server-core/src/utils/certificateUtils.tsx @@ -266,10 +266,6 @@ const writeToTempFile = async (content: string): Promise => { 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 => { + if (!(await hasAuthToken())) { return generateAuthToken(); } @@ -324,6 +320,10 @@ export const getAuthToken = async () => { return token.toString(); }; +export const hasAuthToken = async (): Promise => { + return fs.pathExists(serverAuthToken); +}; + export const validateAuthToken = (token: string) => { if (!serverConfig) { throw new Error(