mirror of https://github.com/embarklabs/embark.git
fix(@embark): single use tokens
This commit is contained in:
parent
ac32cdbfc7
commit
6aa8781ff5
|
@ -19,7 +19,9 @@ function action(type, payload = {}) {
|
||||||
export const AUTHENTICATE = createRequestTypes('AUTHENTICATE');
|
export const AUTHENTICATE = createRequestTypes('AUTHENTICATE');
|
||||||
export const authenticate = {
|
export const authenticate = {
|
||||||
request: (host, token) => action(AUTHENTICATE[REQUEST], {host, token}),
|
request: (host, token) => action(AUTHENTICATE[REQUEST], {host, token}),
|
||||||
success: (_result, payload) => action(AUTHENTICATE[SUCCESS], {host: payload.host, token: payload.token}),
|
success: (result, payload) => {
|
||||||
|
return action(AUTHENTICATE[SUCCESS], {host: payload.host, token: result.token})
|
||||||
|
},
|
||||||
failure: (error) => action(AUTHENTICATE[FAILURE], {error})
|
failure: (error) => action(AUTHENTICATE[FAILURE], {error})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ import {
|
||||||
changeTheme, fetchTheme
|
changeTheme, fetchTheme
|
||||||
} from '../actions';
|
} from '../actions';
|
||||||
|
|
||||||
|
|
||||||
import {LIGHT_THEME, DARK_THEME} from '../constants';
|
import {LIGHT_THEME, DARK_THEME} from '../constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import keccak from "keccakjs";
|
||||||
|
|
||||||
function hash(cnonce, token, type, path, params = {}) {
|
function hash(cnonce, token, type, path, params = {}) {
|
||||||
let hash = new keccak();
|
let hash = new keccak();
|
||||||
hash.update(JSON.stringify(cnonce));
|
hash.update(cnonce.toString());
|
||||||
hash.update(token);
|
hash.update(token);
|
||||||
hash.update(type.toUpperCase());
|
hash.update(type.toUpperCase());
|
||||||
hash.update(`/embark-api${path}`);
|
hash.update(`/embark-api${path}`);
|
||||||
|
@ -52,6 +52,13 @@ function destroy() {
|
||||||
return request('delete', ...arguments);
|
return request('delete', ...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function websocket(credentials, path) {
|
||||||
|
const cnonce = Date.now() + Math.random();
|
||||||
|
const requestHash = hash(cnonce, credentials.token, 'ws', '/', {});
|
||||||
|
|
||||||
|
return new WebSocket(`ws://${credentials.host}/embark-api${path}`, [`${cnonce}|${requestHash}`]);
|
||||||
|
}
|
||||||
|
|
||||||
export function postCommand() {
|
export function postCommand() {
|
||||||
return post('/command', ...arguments);
|
return post('/command', ...arguments);
|
||||||
}
|
}
|
||||||
|
@ -222,15 +229,15 @@ export function toggleBreakpoint(payload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listenToDebugger(credentials) {
|
export function listenToDebugger(credentials) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/debugger`, [credentials.token]);
|
return websocket(credentials, '/debugger');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listenToChannel(credentials, channel) {
|
export function listenToChannel(credentials, channel) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/communication/listenTo/${channel}`, [credentials.token]);
|
return websocket(credentials, `/communication/listenTo/${channel}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function webSocketProcess(credentials, processName) {
|
export function webSocketProcess(credentials, processName) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/process-logs/${processName}`, [credentials.token]);
|
return websocket(credentials, `/process-logs/${processName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function webSocketServices(credentials) {
|
export function webSocketServices(credentials) {
|
||||||
|
@ -238,7 +245,7 @@ export function webSocketServices(credentials) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function webSocketContractLogs(credentials) {
|
export function webSocketContractLogs(credentials) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/contracts/logs`, [credentials.token]);
|
return websocket(credentials, `/contracts/logs`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function webSocketContracts(credentials) {
|
export function webSocketContracts(credentials) {
|
||||||
|
@ -246,13 +253,13 @@ export function webSocketContracts(credentials) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function webSocketContractEvents(credentials) {
|
export function webSocketContractEvents(credentials) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/contracts/event`, [credentials.token]);
|
return websocket(credentials, `/blockchain/contracts/event`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function webSocketBlockHeader(credentials) {
|
export function webSocketBlockHeader(credentials) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/blockHeader`, [credentials.token]);
|
return websocket(credentials, `/blockchain/blockHeader`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function websocketGasOracle(credentials) {
|
export function websocketGasOracle(credentials) {
|
||||||
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/gas/oracle`, [credentials.token]);
|
return websocket(credentials, `/blockchain/gas/oracle`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const uuid = require('uuid/v1');
|
const uuid = require('uuid/v4');
|
||||||
const utils = require("../../utils/utils.js");
|
const utils = require("../../utils/utils.js");
|
||||||
const keccak = require('keccakjs');
|
const keccak = require('keccakjs');
|
||||||
|
|
||||||
|
@ -6,25 +6,39 @@ const ERROR_OBJ = {error: __('Wrong authentication token. Get your token from th
|
||||||
|
|
||||||
class Authenticator {
|
class Authenticator {
|
||||||
constructor(embark, _options) {
|
constructor(embark, _options) {
|
||||||
this.authToken = uuid();
|
|
||||||
this.embark = embark;
|
this.embark = embark;
|
||||||
this.logger = embark.logger;
|
this.logger = embark.logger;
|
||||||
this.events = embark.events;
|
this.events = embark.events;
|
||||||
|
|
||||||
|
this.authToken = uuid();
|
||||||
|
this.emittedTokens = {};
|
||||||
|
|
||||||
this.registerCalls();
|
this.registerCalls();
|
||||||
this.registerEvents();
|
this.registerEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
generateRequestHash(req) {
|
getRemoteAddress(req) {
|
||||||
let cnonce = req.headers['x-embark-cnonce'];
|
return (req.headers && req.headers['x-forwarded-for']) ||
|
||||||
let hash = new keccak();
|
(req.connection && req.connection.remoteAddress) ||
|
||||||
|
(req.socket && req.socket.remoteAddress) ||
|
||||||
|
(req._socket && req._socket.remoteAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateRequestHash(req, token) {
|
||||||
|
const remoteAddress = this.getRemoteAddress(req);
|
||||||
|
const cnonce = req.headers['x-embark-cnonce'];
|
||||||
|
|
||||||
|
// We fallback to an empty string so that the hashing won't fail.
|
||||||
|
token = token || this.emittedTokens[remoteAddress] || '';
|
||||||
|
|
||||||
let url = req.url;
|
let url = req.url;
|
||||||
let queryParamIndex = url.indexOf('?');
|
const queryParamIndex = url.indexOf('?');
|
||||||
url = url.substring(0, queryParamIndex !== -1 ? queryParamIndex : url.length);
|
url = url.substring(0, queryParamIndex !== -1 ? queryParamIndex : url.length);
|
||||||
|
|
||||||
|
let hash = new keccak();
|
||||||
hash.update(cnonce);
|
hash.update(cnonce);
|
||||||
hash.update(this.authToken);
|
hash.update(token);
|
||||||
hash.update(req.method);
|
hash.update(req.method.toUpperCase());
|
||||||
hash.update(url);
|
hash.update(url);
|
||||||
return hash.digest('hex');
|
return hash.digest('hex');
|
||||||
}
|
}
|
||||||
|
@ -36,14 +50,23 @@ class Authenticator {
|
||||||
'post',
|
'post',
|
||||||
'/embark-api/authenticate',
|
'/embark-api/authenticate',
|
||||||
(req, res) => {
|
(req, res) => {
|
||||||
let hash = self.generateRequestHash(req);
|
let hash = self.generateRequestHash(req, this.authToken);
|
||||||
if(hash !== req.headers['x-embark-request-hash']) {
|
if(hash !== req.headers['x-embark-request-hash']) {
|
||||||
this.logger.warn(__('Someone tried and failed to authenticate to the backend'));
|
this.logger.warn(__('Someone tried and failed to authenticate to the backend'));
|
||||||
this.logger.warn(__('- User-Agent: %s', req.headers['user-agent']));
|
this.logger.warn(__('- User-Agent: %s', req.headers['user-agent']));
|
||||||
this.logger.warn(__('- Referer: %s', req.headers.referer));
|
this.logger.warn(__('- Referer: %s', req.headers.referer));
|
||||||
return res.send(ERROR_OBJ);
|
return res.send(ERROR_OBJ);
|
||||||
}
|
}
|
||||||
res.send({});
|
|
||||||
|
// Generate another authentication token.
|
||||||
|
this.authToken = uuid();
|
||||||
|
this.events.request('authenticator:displayUrl', false);
|
||||||
|
|
||||||
|
// Register token for this connection, and send it through.
|
||||||
|
const emittedToken = uuid();
|
||||||
|
const remoteAddress = this.getRemoteAddress(req);
|
||||||
|
this.emittedTokens[remoteAddress] = emittedToken;
|
||||||
|
res.send({token: emittedToken});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -62,20 +85,38 @@ class Authenticator {
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
this.events.once('outputDone', () => {
|
this.events.once('outputDone', () => {
|
||||||
|
this.events.request('authenticator:displayUrl', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.events.setCommandHandler('authenticator:displayUrl', (firstOutput) => {
|
||||||
const {protocol, port, host, enabled} = this.embark.config.webServerConfig;
|
const {protocol, port, host, enabled} = this.embark.config.webServerConfig;
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
if(!firstOutput) this.logger.info(__('Previous token has now been used.'));
|
||||||
this.logger.info(__('Access the web backend with the following url: %s',
|
this.logger.info(__('Access the web backend with the following url: %s',
|
||||||
(`${protocol}://${host}:${port}/embark?token=${this.authToken}`.underline)));
|
(`${protocol}://${host}:${port}/embark?token=${this.authToken}`.underline)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.events.setCommandHandler('authenticator:authorize', (req, res, cb) => {
|
this.events.setCommandHandler('authenticator:authorize', (req, res, cb) => {
|
||||||
|
// HACK
|
||||||
|
if(res.send && req.url === '/embark-api/authenticate') return cb();
|
||||||
|
|
||||||
let authenticated = false;
|
let authenticated = false;
|
||||||
|
|
||||||
if(!res.send) {
|
if(!res.send) {
|
||||||
authenticated = (this.authToken === req.protocol);
|
const [cnonce, hash] = req.protocol.split('|');
|
||||||
|
const computedHash = this.generateRequestHash({
|
||||||
|
headers: {
|
||||||
|
'x-forwarded-for': this.getRemoteAddress(req),
|
||||||
|
'x-embark-cnonce': cnonce
|
||||||
|
},
|
||||||
|
url: '/embark-api/',
|
||||||
|
method: 'ws'
|
||||||
|
});
|
||||||
|
authenticated = (hash === computedHash);
|
||||||
} else {
|
} else {
|
||||||
let hash = self.generateRequestHash(req);
|
const hash = self.generateRequestHash(req);
|
||||||
authenticated = (hash === req.headers['x-embark-request-hash']);
|
authenticated = (hash === req.headers['x-embark-request-hash']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue