Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

backtrace-service

Package Overview
Dependencies
Maintainers
5
Versions
153
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

backtrace-service - npm Package Compare versions

Comparing version 1.0.3 to 1.0.4

160

index.js
const bodyParser = require('body-parser')
const request = require('request')
const crypto = require('crypto');
function apiServiceRegister(options, req_obj, res) {
function genHmac(secret, nonce) {
return crypto.createHmac('sha256', secret).update(nonce).digest('hex');
}
function checkHmac(secret, nonce, proposed_hmac) {
return genHmac(secret, nonce) === proposed_hmac;
}
function log(svcOpts, level, msg) {
if (svcOpts.logger && svcOpts.logger.log)
svcOpts.logger.log(level, msg);
}
function sendError(svcOpts, req, res, code, result) {
const res_obj = Object.assign({status: 'error'}, result);
log(svcOpts, 'warn', `${req.ip}: error ${code}: ${JSON.stringify(res_obj)}`);
res.status(code).send(res_obj);
}
function missingParam(svcOpts, req, res, param) {
sendError(svcOpts, req, res, 400, {error: `missing ${param} parameter`});
}
function missingInternalParam(svcOpts, req, res, param) {
log(svcOpts, 'error', `${req.ip}: error: missing internal param ${param}`);
sendError(svcOpts, req, res, 500, {error: "internal error"});
}
function apiServiceRegister(svcOpts, req, req_obj, res) {
var stoken;
if (!req_obj.url) {
res.status(400).send({status: "error", error: "missing url parameter"});
return;
}
if (!req_obj.url)
return missingParam(svcOpts, req, res, "url");
if (!req_obj.nonce)
return missingParam(svcOpts, req, res, "nonce");
if (!req_obj.hmac)
return missingParam(svcOpts, req, res, "hmac");
if (options.authTokens.includes(req_obj.token) === false) {
res.status(403).send({status: "error", error: "invalid token"});
if (!checkHmac(svcOpts.secret, req_obj.nonce, req_obj.hmac)) {
sendError(svcOpts, req, res, 403, {error: "unauthenticated request"});
return;
}
options.coronerdCallback(req_obj.url);
if (svcOpts.coronerdCallback)
svcOpts.coronerdCallback(req_obj.url);
res.json({

@@ -22,2 +56,14 @@ status: "ok",

function checkOptions(svcOpts, caller) {
if (!svcOpts) {
throw new TypeError(`${caller}() requires options`);
}
if (typeof(svcOpts.name) !== 'string' || svcOpts.name.length === 0) {
throw new TypeError(`${caller}() requires options.name string`);
}
if (typeof(svcOpts.secret) !== 'string' || svcOpts.secret.length === 0) {
throw new TypeError(`${caller}() requires options.secret string`);
}
}
/*

@@ -27,16 +73,8 @@ * Generate a middleware handler for Express.js, using options to hook the

*/
function serviceRequest(options) {
if (!options) {
throw new TypeError("serviceRequest() requires options");
}
if (!Array.isArray(options.authTokens)) {
throw new TypeError("serviceRequest() requires options.authTokens array");
}
if (typeof(options.coronerdCallback) !== 'function') {
throw new TypeError("serviceRequest() requires options.coronerdCallback");
}
function serviceRequest(svcOpts) {
checkOptions(svcOpts, 'serviceRequest');
/*
* Handle <prefix>/service requests. These require action and token QS.
* The token serves as an authentication mechanism.
* Handle <prefix>/service requests. These require action QS. Other QS
* are defined per action.
*/

@@ -46,14 +84,8 @@ return [ bodyParser.json(), (req, res, next) => {

if (!req_obj.action) {
res.status(400).send({status: "error", error: "missing action parameter"});
return;
}
if (!req_obj.token) {
res.status(400).send({status: "error", error: "missing token parameter"});
return;
}
if (!req_obj.action)
return missingParam(svcOpts, req, res, "action");
switch (req_obj.action) {
case "register":
return apiServiceRegister(options, req_obj, res);
return apiServiceRegister(svcOpts, req, req_obj, res);
default:

@@ -69,6 +101,76 @@ break;

/*
* Generate a middleware for Express.js, which authenticates a request given
* its URL and session token, provided by an earlier callback in
* req.coronerAuth. The parameters required are:
*
* - url: the full URL to the coronerd (https://foo.sp.backtrace.io/)
* - token: the user's session token (aka X-Coroner-Token)
*
* Upon success, the configuration data will be in req.coronerAuth.cfg.
*/
function authenticateRequest(svcOpts) {
checkOptions(svcOpts, 'authenticateRequest');
return [ (req, res, next) => {
if (!req.coronerAuth)
return missingInternalParam(svcOpts, req, res, "req.coronerAuth");
if (!req.coronerAuth.url)
return missingInternalParam(svcOpts, req, res, "req.coronerAuth.url");
if (!req.coronerAuth.token)
return missingInternalParam(svcOpts, req, res, "req.coronerAuth.token");
const req_opts = {
url: `${req.coronerAuth.url}/api/config`,
headers: {
'X-Service-Name': svcOpts.name,
'X-Coroner-Token': req.coronerAuth.token,
'X-Service-HMAC': genHmac(svcOpts.secret, req.coronerAuth.token),
}
};
request(req_opts, (error, response, body) => {
if (error) {
next(error);
return;
}
if (response.statusCode !== 200) {
next(new Error(`Invalid server response code ${response.statusCode}`));
return;
}
/* Verify the coronerd knows the service secret. */
const coronerd_nonce = response.headers['x-service-nonce'];
const coronerd_hmac = response.headers['x-service-hmac'];
if (!coronerd_nonce || !coronerd_hmac) {
next(new Error("Invalid server response, missing headers"));
return;
}
if (!checkHmac(svcOpts.secret, coronerd_nonce, coronerd_hmac)) {
next(new Error("Invalid server generated HMAC"));
return;
}
/* Success, parse the response body and forward result to next. */
var bodyj;
try {
bodyj = JSON.parse(body);
} catch (e) {
next(new Error("auth parse failure"));
}
req.coronerAuth.cfg = bodyj;
if (!bodyj.user) {
const error = bodyj.error ? bodyj.error : new Error('auth failure');
next(error);
} else {
next();
}
});
} ];
}
module.exports = {
serviceRequest: serviceRequest,
authenticateRequest: authenticateRequest,
};
// vim:ts=2:sw=2:et

2

package.json
{
"name": "backtrace-service",
"version": "1.0.3",
"version": "1.0.4",
"description": "Backtrace Service middleware",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -10,5 +10,5 @@ # Backtrace Service Layer nodejs library

const btservice = require('backtrace-service');
const atokens = ["asdfghjk"];
app.post("${url_prefix}/service", btservice.serviceRequest({
authTokens: atokens,
name: 'simple',
secret: 'asdfghjk',
coronerdCallback: (url) => {

@@ -20,7 +20,8 @@ console.log('heard from coronerd at: ' + url);

## Options for serviceRequest
### Options for serviceRequest
The following options are accepted as the sole argument for the call:
* authTokens: The list of authentication tokens to accept from coronerd
instances attempting to register themselves with the service.
* name: Name of the service (usually its type).
* secret: The shared secret that the service will use to authenticate
incoming requests and config replies from a coronerd.
* coronerdCallback: A callback that takes an URL parameter, and performs any

@@ -43,1 +44,28 @@ service specific setup associated with integrating a new coronerd instance.

`/api/config`, to reach that resource on the coronerd instance.
## authenticateRequest usage
This function is intended as an additional middleware which may be used in
application routes to validate requests that involve a session token issued
by a remote coronerd. The actual call can reuse the same options argument
used for `serviceRequest`, although it does not use `coronerdCallback`.
In the route middleware list, prior to `authenticateRequest`, the
application must attach a `req.coronerAuth` object which contains:
* url: The full URL to the remote coronerd instance.
* token: The user's session token to be validated.
For example:
```
req.coronerAuth = {
url: "https://backtrace.sp.backtrace.io/",
token: "f5af46b8eb32adb860ef46a9e714cfde",
}
```
This normalized form is used due to the fact that different services take
these parameters from clients in different ways.
Middlewares that come after `authenticateRequest` will have access to the
validated coronerd `/api/config` response in `req.coronerAuth.cfg`.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc