Skip to content

Commit

Permalink
Merge pull request #147 from plivo/VT-1798-v3-signature
Browse files Browse the repository at this point in the history
adds utility functions for v3 signature
  • Loading branch information
nixonsam authored Apr 29, 2020
2 parents c3e6d1d + 859fa78 commit feb8c36
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Change Log

## [4.6.0](https://github.com/plivo/plivo-node/releases/tag/v4.6.0)(2020-04-29)
- Add V3 signature helper functions.

## [4.5.2](https://github.com/plivo/plivo-node/releases/tag/v4.5.2)(2020-04-28)
- Fix List Conferences API response.

Expand Down
5 changes: 5 additions & 0 deletions lib/rest/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { PricingInterface } from "../resources/pricings";
import { RecordingInterface } from "../resources/recordings";
import { Response } from "../utils/plivoxml";
import { validateSignature } from "../utils/security";
import { validateV3Signature } from "../utils/v3Security";
import { stringify } from "./../utils/jsonStrinfigier";
import { CallFeedbackInterface } from "../resources/callFeedback";
import { MediaInterface } from "../resources/media.js";
Expand All @@ -22,6 +23,10 @@ exports.Response = function() {
return new Response();
};

exports.validateV3Signature = function(method, uri, nonce, auth_token, v3_signature, params={}) {
return validateV3Signature(method, uri, nonce, auth_token, v3_signature, params);
};

exports.validateSignature = function(uri, nonce, signature, auth_token) {
return validateSignature(uri, nonce, signature, auth_token);
};
Expand Down
111 changes: 111 additions & 0 deletions lib/utils/v3Security.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//@flow
const utf8 = require('utf8');
const buildUrl = require('build-url');
const base64 = require('base-64');
const qs = require('querystring');
import * as parser from 'uri-parser';
import crypto from 'crypto';
import _ from 'lodash';

function get_map_from_query(params1, params2) {
let params = {};
Object.keys(params1).forEach(function (key) {
let val = params1[key];
if (val instanceof Array) {
params[key] = val
} else {
params[key] = [val]
}
});
Object.keys(params2).forEach(function (key) {
let val = params2[key];
if (!(val instanceof Array)) {
val = [val];
}
if (key in params) {
params[key] = params[key].concat(val)
} else {
params[key] = val
}
});
return params;
}

function get_sorted_query_string(params) {
let query_string = [];
Object.keys(params).sort().forEach(function (key) {
let val = params[key];
val.sort().forEach(function (value) {
query_string.push(key.toString() + '=' + value.toString());
});
});
return query_string.join('&');
}

function get_sorted_params_string(params) {
let paramsString = [];
Object.keys(params).sort().forEach(function (key) {
let val = params[key];
if (val instanceof Array) {
val.sort().forEach(function (value) {
paramsString.push(key.toString() + value.toString());
});
} else {
paramsString.push(key.toString() + val.toString());
}
});
return paramsString.join('')
}

function construct_get_url(uri, params, empty_post_params=true) {
let parsed_uri = parser.parse(uri);
let url_protocol = parsed_uri.protocol === '' ? 'http://' : parsed_uri.protocol+'://';
let proxy = parsed_uri.port === '' ? parsed_uri.host : parsed_uri.host + ':' + parsed_uri.port;
let base_url = buildUrl(url_protocol + proxy , { path: parsed_uri.path });
params = get_map_from_query(qs.parse(parsed_uri.query), params);
let query_params = get_sorted_query_string(params);
if (query_params.length > 0 || !empty_post_params) {
base_url = base_url + '?' + query_params;
}
if (query_params.length > 0 && !empty_post_params) {
base_url = base_url + '.';
}
return base_url;
}

function construct_post_url(uri, params) {
let base_url = construct_get_url(uri, {}, _.isEmpty(params));
return base_url + get_sorted_params_string(params);
}

function get_signature_v3(auth_token, base_url, nonce) {
base_url = base_url + '.' + nonce;
let hmac = crypto.createHmac('sha256', auth_token);
let hmacBytes = base64.decode(hmac.update(base_url).digest('base64'));
return base64.encode(hmacBytes);
}

export function validateV3Signature(method: string, uri: string,
nonce: string, auth_token: string,
v3_signature: string, params={}) {
auth_token = utf8.encode(auth_token);
nonce = utf8.encode(nonce);
v3_signature = utf8.encode(v3_signature);
uri = utf8.encode(uri);
let base_url = uri;
if (method === 'GET') {
base_url = construct_get_url(uri, params);
} else if (method === 'POST') {
base_url = construct_post_url(uri, params);
} else {
throw new Error("Please provide authToken");
}
let signature = get_signature_v3(auth_token, base_url, nonce);
let matched = false;
_.split(v3_signature, ',').forEach(function (plivo_sign) {
if (plivo_sign === signature) {
matched = true;
}
});
return matched;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "plivo",
"version": "4.5.2",
"version": "4.6.0",
"description": "A Node.js SDK to make voice calls and send SMS using Plivo and to generate Plivo XML",
"homepage": "https://github.com/plivo/plivo-node",
"files": [
Expand Down

0 comments on commit feb8c36

Please sign in to comment.