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:
committed by
Facebook GitHub Bot
parent
69378c4b09
commit
8ef29c8160
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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}`);
|
||||||
@@ -212,7 +211,7 @@
|
|||||||
setTimeout(() => retry(retries), 1000);
|
setTimeout(() => retry(retries), 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retry(3);
|
retry(3);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -117,7 +118,7 @@
|
|||||||
setTimeout(() => retry(retries), 1000);
|
setTimeout(() => retry(retries), 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retry(3);
|
retry(3);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user