Embed auth token into HTML

Summary:
Auth token used be injected in the manifest file. Instead, have the server injected into the main HTML page.

The main driver to this change are:
- Simplify
- There are instances in which for some reason reading/writing the token from the manifest fails. This will address that problem.

Reviewed By: lblasa

Differential Revision: D51160521

fbshipit-source-id: 4626fd8f56bc8b61182a53a5d9cf5acad1e723bc
This commit is contained in:
Andrey Goncharov
2023-11-09 14:05:43 -08:00
committed by Facebook GitHub Bot
parent 69378c4b09
commit 8ef29c8160
6 changed files with 25 additions and 69 deletions

View File

@@ -16,11 +16,10 @@ import {
} from './openssl-wrapper-with-promises'; } from './openssl-wrapper-with-promises';
import path from 'path'; import path from 'path';
import tmp, {FileOptions} from 'tmp'; import tmp, {FileOptions} from 'tmp';
import {FlipperServerConfig, reportPlatformFailures} from 'flipper-common'; import {reportPlatformFailures} from 'flipper-common';
import {isTest} from 'flipper-common'; import {isTest} from 'flipper-common';
import {flipperDataFolder} from '../../utils/paths'; import {flipperDataFolder} from '../../utils/paths';
import * as jwt from 'jsonwebtoken'; import * as jwt from 'jsonwebtoken';
import {getFlipperServerConfig} from '../../FlipperServerConfig';
import {Mutex} from 'async-mutex'; import {Mutex} from 'async-mutex';
import {createSecureContext} from 'tls'; import {createSecureContext} from 'tls';
@@ -288,52 +287,6 @@ const writeToTempFile = async (content: string): Promise<string> => {
await fs.writeFile(path, content); await fs.writeFile(path, content);
return path; return path;
}; };
const manifestFilename = 'manifest.json';
const getManifestPath = (config: FlipperServerConfig): string => {
return path.resolve(config.paths.staticPath, manifestFilename);
};
const exportTokenToManifest = async (token: string) => {
console.info('Export token to manifest');
let config: FlipperServerConfig | undefined;
try {
config = getFlipperServerConfig();
} catch {
console.warn(
'Unable to obtain server configuration whilst exporting token to manifest',
);
}
if (!config || !config.environmentInfo.isHeadlessBuild) {
console.warn(
'No configuration or not headless build detected, skipping exporting token to manifest',
config,
);
return;
}
const manifestPath = getManifestPath(config);
try {
console.info('Reading manifest at path', manifestPath);
const manifestData = await fs.readFile(manifestPath, {
encoding: 'utf-8',
});
const manifest = JSON.parse(manifestData);
manifest.token = token;
const newManifestData = JSON.stringify(manifest, null, 4);
console.info('Export token to manifest at path', manifestPath);
await fs.writeFile(manifestPath, newManifestData);
} catch (e) {
console.error(
'Unable to export authentication token to manifest, may be non existent.',
e,
);
}
};
export const generateAuthToken = async () => { export const generateAuthToken = async () => {
console.info('Generate client authentication token'); console.info('Generate client authentication token');
@@ -347,8 +300,6 @@ export const generateAuthToken = async () => {
await fs.writeFile(serverAuthToken, token); await fs.writeFile(serverAuthToken, token);
await exportTokenToManifest(token);
return token; return token;
}; };
@@ -382,8 +333,6 @@ export const getAuthToken = async (): Promise<string> => {
return generateAuthToken(); return generateAuthToken();
} }
await exportTokenToManifest(token);
return token; return token;
}; };

View File

@@ -18,7 +18,10 @@ import exitHook from 'exit-hook';
import {attachSocketServer} from './attachSocketServer'; import {attachSocketServer} from './attachSocketServer';
import {FlipperServerImpl} from '../FlipperServerImpl'; import {FlipperServerImpl} from '../FlipperServerImpl';
import {FlipperServerCompanionEnv} from 'flipper-server-companion'; import {FlipperServerCompanionEnv} from 'flipper-server-companion';
import {validateAuthToken} from '../app-connectivity/certificate-exchange/certificate-utils'; import {
getAuthToken,
validateAuthToken,
} from '../app-connectivity/certificate-exchange/certificate-utils';
import {tracker} from '../tracker'; import {tracker} from '../tracker';
import {EnvironmentInfo, isProduction} from 'flipper-common'; import {EnvironmentInfo, isProduction} from 'flipper-common';
import {GRAPH_SECRET} from '../fb-stubs/constants'; import {GRAPH_SECRET} from '../fb-stubs/constants';
@@ -38,6 +41,7 @@ type ReadyForConnections = (
const verifyAuthToken = (req: http.IncomingMessage): boolean => { const verifyAuthToken = (req: http.IncomingMessage): boolean => {
let token: string | null = null; let token: string | null = null;
if (req.url) { if (req.url) {
const url = new URL(req.url, `http://${req.headers.host}`); const url = new URL(req.url, `http://${req.headers.host}`);
token = url.searchParams.get('token'); token = url.searchParams.get('token');
@@ -47,6 +51,10 @@ const verifyAuthToken = (req: http.IncomingMessage): boolean => {
token = req.headers['x-access-token'] as string; token = req.headers['x-access-token'] as string;
} }
if (!isProduction()) {
console.info('[conn] verifyAuthToken -> token', token);
}
if (!token) { if (!token) {
console.warn('[conn] A token is required for authentication'); console.warn('[conn] A token is required for authentication');
tracker.track('server-auth-token-verification', { tracker.track('server-auth-token-verification', {
@@ -146,16 +154,18 @@ async function startHTTPServer(
next(); next();
}); });
app.get('/', (_req, res) => { app.get('/', async (_req, res) => {
const resource = isReady const resource = isReady
? path.join(config.staticPath, config.entry) ? path.join(config.staticPath, config.entry)
: path.join(config.staticPath, 'loading.html'); : path.join(config.staticPath, 'loading.html');
const token = await getAuthToken();
fs.readFile(resource, (_err, content) => { fs.readFile(resource, (_err, content) => {
const processedContent = content const processedContent = content
.toString() .toString()
.replace('GRAPH_SECRET_REPLACE_ME', GRAPH_SECRET) .replace('GRAPH_SECRET_REPLACE_ME', GRAPH_SECRET)
.replace('FLIPPER_APP_VERSION_REPLACE_ME', environmentInfo.appVersion) .replace('FLIPPER_APP_VERSION_REPLACE_ME', environmentInfo.appVersion)
.replace('FLIPPER_UNIXNAME_REPLACE_ME', environmentInfo.os.unixname) .replace('FLIPPER_UNIXNAME_REPLACE_ME', environmentInfo.os.unixname)
.replace('FLIPPER_AUTH_TOKEN_REPLACE_ME', token)
.replace('FLIPPER_SESSION_ID_REPLACE_ME', sessionId); .replace('FLIPPER_SESSION_ID_REPLACE_ME', sessionId);
res.end(processedContent); res.end(processedContent);
}); });

View File

@@ -22,6 +22,7 @@ declare global {
FLIPPER_APP_VERSION: string; FLIPPER_APP_VERSION: string;
FLIPPER_SESSION_ID: string; FLIPPER_SESSION_ID: string;
FLIPPER_UNIXNAME: string; FLIPPER_UNIXNAME: string;
FLIPPER_AUTH_TOKEN: string;
flipperShowMessage?(message: string): void; flipperShowMessage?(message: string): void;
flipperHideMessage?(): void; flipperHideMessage?(): void;

View File

@@ -55,17 +55,12 @@ async function start() {
const providerParams = new URL(location.href).searchParams; const providerParams = new URL(location.href).searchParams;
let token = providerParams.get('token'); let token = providerParams.get('token');
if (!token) { if (!token) {
console.info( console.info('[flipper-client][ui-browser] Get token from HTML instead');
'[flipper-client][ui-browser] Get token from manifest instead', token = window.FLIPPER_AUTH_TOKEN;
); if (!token || token === 'FLIPPER_AUTH_TOKEN_REPLACE_ME') {
try {
const manifestResponse = await fetch('manifest.json');
const manifest = await manifestResponse.json();
token = manifest.token;
} catch (e) {
console.warn( console.warn(
'[flipper-client][ui-browser] Failed to get token from manifest. Error:', '[flipper-client][ui-browser] Failed to get token from HTML',
e.message, token,
); );
} }
} }
@@ -73,6 +68,7 @@ async function start() {
getLogger().info( getLogger().info(
'[flipper-client][ui-browser] Token is available: ', '[flipper-client][ui-browser] Token is available: ',
token?.length != 0, token?.length != 0,
token?.length === 460,
); );
return token; return token;

View File

@@ -141,13 +141,12 @@
window.FLIPPER_APP_VERSION = 'FLIPPER_APP_VERSION_REPLACE_ME'; window.FLIPPER_APP_VERSION = 'FLIPPER_APP_VERSION_REPLACE_ME';
window.FLIPPER_SESSION_ID = 'FLIPPER_SESSION_ID_REPLACE_ME'; window.FLIPPER_SESSION_ID = 'FLIPPER_SESSION_ID_REPLACE_ME';
window.FLIPPER_UNIXNAME = 'FLIPPER_UNIXNAME_REPLACE_ME'; window.FLIPPER_UNIXNAME = 'FLIPPER_UNIXNAME_REPLACE_ME';
window.FLIPPER_AUTH_TOKEN = 'FLIPPER_AUTH_TOKEN_REPLACE_ME';
const params = new URL(location.href).searchParams; const params = new URL(location.href).searchParams;
let token = params.get('token'); let token = params.get('token');
if (!token) { if (!token) {
const manifestResponse = await fetch('manifest.json'); token = window.FLIPPER_AUTH_TOKEN
const manifest = await manifestResponse.json();
token = manifest.token;
} }
const socket = new WebSocket(`ws://${location.host}?token=${token}`); const socket = new WebSocket(`ws://${location.host}?token=${token}`);

View File

@@ -90,6 +90,7 @@
window.FLIPPER_APP_VERSION = 'FLIPPER_APP_VERSION_REPLACE_ME'; window.FLIPPER_APP_VERSION = 'FLIPPER_APP_VERSION_REPLACE_ME';
window.FLIPPER_SESSION_ID = 'FLIPPER_SESSION_ID_REPLACE_ME'; window.FLIPPER_SESSION_ID = 'FLIPPER_SESSION_ID_REPLACE_ME';
window.FLIPPER_UNIXNAME = 'FLIPPER_UNIXNAME_REPLACE_ME'; window.FLIPPER_UNIXNAME = 'FLIPPER_UNIXNAME_REPLACE_ME';
window.FLIPPER_AUTH_TOKEN = 'FLIPPER_AUTH_TOKEN_REPLACE_ME';
// load correct theme (n.b. this doesn't handle system value specifically, will assume light in such cases) // load correct theme (n.b. this doesn't handle system value specifically, will assume light in such cases)
try { try {