UNPKG

mastercard-api-core

Version:
287 lines (236 loc) 8.35 kB
var fs = require('fs'); var crypto = require('crypto'); var URL = require('url'); var queryString = require('querystring'); var forge = require('node-forge'); var utils = require('../../utils'); var error = require('../../error'); var OAuthParameters = require('./oauth-parameters'); /** * Create an OAuth security object for sending API requests * * @param consumerKey - consumer key from MasterCard Developer Zone * @param p12Path - path to .p12 file * @param alias - key alias for p12 key * @param password - password for p12 key * @constructor */ function OAuth(consumerKey, p12Path, alias, password, optionalParam = 0) { if(!utils.isSet(consumerKey)) { throw new error.SDKError('Consumer key is not set'); } if(!utils.isSet(p12Path)) { throw new error.SDKError('Path to .p12 file is not set'); } if(!utils.isSet(alias)) { throw new error.SDKError('Key alias is not set'); } if(!utils.isSet(password)) { throw new error.SDKError('Keystore password is not set'); } this.consumerKey = consumerKey; // Read p12 key store content from disk var p12Content = fs.readFileSync(p12Path, 'binary'); this.privateKey = _getPrivateKey(p12Content, alias, password); this.sign = sign; } /** * Create an OAuth security object for sending API requests * * @param consumerKey - consumer key from MasterCard Developer Zone * @param p12Content - string content of p12 key store * @param alias - key alias for p12 key * @param password - password for p12 key * @constructor */ function OAuthWithFileContent(consumerKey, p12Content, alias, password) { if(!utils.isSet(consumerKey)) { throw new error.SDKError('Consumer key is not set'); } if(!p12Content || p12Content.length <= 1) { throw new error.SDKError('p12 key store content is empty'); } if(!utils.isSet(alias)) { throw new error.SDKError('Key alias is not set'); } if(!utils.isSet(password)) { throw new error.SDKError('Keystore password is not set'); } this.consumerKey = consumerKey; this.privateKey = _getPrivateKey(p12Content, alias, password); this.sign = sign; } /** * Sign the HTTP Request and return OAuth header value * @param uri * @param httpMethod * @param body * @returns {String} OAuth header value */ var sign = function (uri, httpMethod, body) { var oauthParams = new OAuthParameters(this.consumerKey); oauthParams.generateBodyHash(body); var signatureBaseString = _generateSignatureBaseString(uri.href, httpMethod, oauthParams); // signatureBaseString = _postProcessSignatureBaseString(signatureBaseString); return _signRequest(oauthParams, this.privateKey, signatureBaseString) }; /** * Read the private key from the supplied p12 file * * @param p12Path * @param alias * @param password * @returns {*} PEM * @private */ var _getPrivateKey = function (p12Content, alias, password) { // Get asn1 from DER var p12Asn1 = forge.asn1.fromDer(p12Content, false); // Get p12 using the password var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, password); // Get Key from p12 var keyObj = p12.getBags({ friendlyName: alias, bagType: forge.pki.oids.pkcs8ShroudedKeyBag }).friendlyName[0]; if(!utils.isSet(keyObj)) { throw new error.SDKError("No key found for alias [" + alias + "]") } var key = keyObj.key; // Get private key as PEM var pem = forge.pki.privateKeyToPem(key); return pem; }; /** * "private method" to generate the signature base string from the URL, request method, and parameters * * @param url - URL to connect to * @param requestMethod - HTTP request method * @param oauthParams - parameters containing authorization information * @returns {string|*} - signature base string generated * @private */ var _generateSignatureBaseString = function (url, requestMethod, oauthParams) { return utils.uriRfc3986Encode(requestMethod.toUpperCase()) + '&' + utils.uriRfc3986Encode((_normalizeUrl(url))) + '&' + utils.uriRfc3986Encode(_normalizeParameters(url, oauthParams)); }; /** * "private" method to sign the signature base string * * @param oauthParams - parameters containing authorization information * @param privateKey - URSA private key object * @param oauthParams * @param privateKey * @param signatureBaseString * @returns {*} authHeader * @private */ var _signRequest = function (oauthParams, privateKey, signatureBaseString) { var signer = crypto.createSign('RSA-SHA256'); signer = signer.update(new Buffer(signatureBaseString)); oauthParams.signature = signer.sign(privateKey, 'base64'); // oauthParams.signature = oauthParams.signature); // oauthParams.signature = oauthParams.signature.replace('+', '%20'); // oauthParams.signature = oauthParams.signature.replace('*', '%2A'); // oauthParams.signature = oauthParams.signature.replace('~', '%7E'); return _buildAuthHeaderString(oauthParams); }; /** * "private" method to build the authorization header from the contents of the oauth parameters * * @param oauthParams - object containing authorization information * @returns {string} - authorization header * @private */ var _buildAuthHeaderString = function (oauthParams) { var header = 'OAuth '; if (oauthParams.bodyHash) { header += 'oauth_body_hash="' + utils.uriRfc3986Encode(oauthParams.bodyHash) + '",'; } header += 'oauth_consumer_key="' + utils.uriRfc3986Encode(oauthParams.consumerKey) + '",'; header += 'oauth_nonce="' + utils.uriRfc3986Encode(oauthParams.nonce) + '",'; header += 'oauth_signature="' + utils.uriRfc3986Encode(oauthParams.signature) + '",'; header += 'oauth_signature_method="' + utils.uriRfc3986Encode(oauthParams.signatureMethod) + '",'; header += 'oauth_timestamp="' + utils.uriRfc3986Encode(oauthParams.timeStamp) + '",'; header += 'oauth_version="' + utils.uriRfc3986Encode(oauthParams.oauthVersion) + '"'; return header; }; /** * "private" method to strip off the querystring parameters and port from the URL * * @param url - url to modify * @returns {*} - normalized URL * @private */ var _normalizeUrl = function (url) { var tmp = url; // strip query portion var idx = url.indexOf('?'); if (idx > 0) { tmp = url.substr(0, idx); } else { tmp = url } // strip port if (tmp.lastIndexOf(':') && tmp.lastIndexOf(':') > 5) { // implies port is given tmp = tmp.substr(0, tmp.lastIndexOf(':')); } return tmp; }; /** * "private" method to put querystring and oauth parameters in lexical order * * @param url - url containing query string parameters * @param oauthParams - object containing authorization info * @returns {string} - normalized parameter string * @private */ var _normalizeParameters = function (url, oauthParams) { var uri = URL.parse(url); // Always has at least ?Format=JSON var qstringHash = uri.search ? queryString.parse(uri.search.split('?')[1]) : []; var oauthHash = oauthParams.generateParametersHash(); var nameArr = []; var idx = 0; for (var qStringKey in qstringHash) { nameArr[idx] = qStringKey; idx++; } for (var oauthKey in oauthHash) { nameArr[idx] = oauthKey; idx++; } nameArr.sort(); // now params are in alphabetical order var parm = ''; for (var i = 0; i < nameArr.length; i++) { if (i > 0) { parm += '&'; } if (qstringHash[nameArr[i]]) { parm += utils.uriRfc3986Encode(nameArr[i]) + '=' + utils.uriRfc3986Encode(qstringHash[nameArr[i]]); } else { parm += utils.uriRfc3986Encode(nameArr[i]) + '=' + utils.uriRfc3986Encode(oauthHash[nameArr[i]]); } } return parm; }; /** * * @param signatureBaseString * @returns {*} * @private */ var _postProcessSignatureBaseString = function (signatureBaseString) { signatureBaseString = signatureBaseString.replace(/%20/g, '%2520'); signatureBaseString = signatureBaseString.replace('!', '%21'); return signatureBaseString }; module.exports = { OAuth: OAuth, OAuthWithFileContent: OAuthWithFileContent };