Skip to content

Commit

Permalink
fix(@embark): single use tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
Andre Medeiros authored and iurimatias committed Dec 16, 2018
1 parent ac32cdb commit 6aa8781
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 22 deletions.
4 changes: 3 additions & 1 deletion embark-ui/src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ function action(type, payload = {}) {
export const AUTHENTICATE = createRequestTypes('AUTHENTICATE');
export const authenticate = {
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})
};

Expand Down
1 change: 0 additions & 1 deletion embark-ui/src/containers/AppContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
changeTheme, fetchTheme
} from '../actions';


import {LIGHT_THEME, DARK_THEME} from '../constants';

import {
Expand Down
23 changes: 15 additions & 8 deletions embark-ui/src/services/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import keccak from "keccakjs";

function hash(cnonce, token, type, path, params = {}) {
let hash = new keccak();
hash.update(JSON.stringify(cnonce));
hash.update(cnonce.toString());
hash.update(token);
hash.update(type.toUpperCase());
hash.update(`/embark-api${path}`);
Expand Down Expand Up @@ -52,6 +52,13 @@ function destroy() {
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() {
return post('/command', ...arguments);
}
Expand Down Expand Up @@ -222,37 +229,37 @@ export function toggleBreakpoint(payload) {
}

export function listenToDebugger(credentials) {
return new WebSocket(`ws://${credentials.host}/embark-api/debugger`, [credentials.token]);
return websocket(credentials, '/debugger');
}

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) {
return new WebSocket(`ws://${credentials.host}/embark-api/process-logs/${processName}`, [credentials.token]);
return websocket(credentials, `/process-logs/${processName}`);
}

export function webSocketServices(credentials) {
return new WebSocket(`ws://${credentials.host}/embark-api/services`, [credentials.token]);
}

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) {
return new WebSocket(`ws://${credentials.host}/embark-api/contracts`, [credentials.token]);
}

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) {
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/blockHeader`, [credentials.token]);
return websocket(credentials, `/blockchain/blockHeader`);
}

export function websocketGasOracle(credentials) {
return new WebSocket(`ws://${credentials.host}/embark-api/blockchain/gas/oracle`, [credentials.token]);
return websocket(credentials, `/blockchain/gas/oracle`);
}
65 changes: 53 additions & 12 deletions src/lib/modules/authenticator/index.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
const uuid = require('uuid/v1');
const uuid = require('uuid/v4');
const utils = require("../../utils/utils.js");
const keccak = require('keccakjs');

const ERROR_OBJ = {error: __('Wrong authentication token. Get your token from the Embark console by typing `token`')};

class Authenticator {
constructor(embark, _options) {
this.authToken = uuid();
this.embark = embark;
this.logger = embark.logger;
this.events = embark.events;

this.authToken = uuid();
this.emittedTokens = {};

this.registerCalls();
this.registerEvents();
}

generateRequestHash(req) {
let cnonce = req.headers['x-embark-cnonce'];
let hash = new keccak();
getRemoteAddress(req) {
return (req.headers && req.headers['x-forwarded-for']) ||
(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 queryParamIndex = url.indexOf('?');
const queryParamIndex = url.indexOf('?');
url = url.substring(0, queryParamIndex !== -1 ? queryParamIndex : url.length);

let hash = new keccak();
hash.update(cnonce);
hash.update(this.authToken);
hash.update(req.method);
hash.update(token);
hash.update(req.method.toUpperCase());
hash.update(url);
return hash.digest('hex');
}
Expand All @@ -36,14 +50,23 @@ class Authenticator {
'post',
'/embark-api/authenticate',
(req, res) => {
let hash = self.generateRequestHash(req);
let hash = self.generateRequestHash(req, this.authToken);
if(hash !== req.headers['x-embark-request-hash']) {
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(__('- Referer: %s', req.headers.referer));
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});
}
);

Expand All @@ -62,20 +85,38 @@ class Authenticator {
let self = this;

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;

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',
(`${protocol}://${host}:${port}/embark?token=${this.authToken}`.underline)));
}
});

this.events.setCommandHandler('authenticator:authorize', (req, res, cb) => {
// HACK
if(res.send && req.url === '/embark-api/authenticate') return cb();

let authenticated = false;

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 {
let hash = self.generateRequestHash(req);
const hash = self.generateRequestHash(req);
authenticated = (hash === req.headers['x-embark-request-hash']);
}

Expand Down

0 comments on commit 6aa8781

Please sign in to comment.