diff --git a/README.md b/README.md index b2dc28f..b785c7b 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,7 @@ $.ajax({ * url: ``String`` * method: ``String`` default ``'GET'`` * data: ``Object`` any custom data you want to send with, including extra oauth option ``oauth_*`` as oauth_callback, oauth_version... +* includeBodyHash: ``Boolean`` default ``false`` set to true if you want ``oauth_body_hash`` signing ```js var request_data = { diff --git a/oauth-1.0a.js b/oauth-1.0a.js index 2367b9e..411a016 100644 --- a/oauth-1.0a.js +++ b/oauth-1.0a.js @@ -78,6 +78,10 @@ OAuth.prototype.authorize = function(request, token) { request.data = {}; } + if(request.includeBodyHash) { + oauth_data.oauth_body_hash = this.getBodyHash(request, token.secret) + } + oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data); return oauth_data; @@ -94,6 +98,16 @@ OAuth.prototype.getSignature = function(request, token_secret, oauth_data) { return this.hash_function(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret)); }; +/** + * Create a OAuth Body Hash + * @param {Object} request data + */ +OAuth.prototype.getBodyHash = function(request, token_secret) { + var body = typeof request.data === 'string' ? request.data : JSON.stringify(request.data) + + return this.hash_function(body, this.getSigningKey(token_secret)) +}; + /** * Base String = Method + Base Url + ParameterString * @param {Object} request data @@ -115,7 +129,12 @@ OAuth.prototype.getBaseString = function(request, oauth_data) { * @return {Object} Parameter string data */ OAuth.prototype.getParameterString = function(request, oauth_data) { - var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url))))); + var base_string_data; + if (oauth_data.oauth_body_hash) { + base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.deParamUrl(request.url)))); + } else { + base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url))))); + } var data_str = ''; diff --git a/package.json b/package.json index 2a16af9..198bef0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oauth-1.0a", - "version": "2.0.0", + "version": "2.1.0", "description": "OAuth 1.0a Request Authorization for Node and Browser.", "scripts": { "test": "make test" diff --git a/test/oauth_body_hash.js b/test/oauth_body_hash.js new file mode 100644 index 0000000..aab1690 --- /dev/null +++ b/test/oauth_body_hash.js @@ -0,0 +1,131 @@ +var expect = require('chai').expect; +var OAuth = require('../oauth-1.0a'); +var crypto = require('crypto'); + +describe("OAuth Body Hash", function() { + var oauth = new OAuth({ + consumer: { + key: '1434affd-4d69-4a1a-bace-cc5c6fe493bc', + secret: '932a216f-fb94-43b6-a2d2-e9c6b345cbea' + }, + signature_method: 'HMAC-SHA1', + hash_function: function(base_string, key) { + return crypto.createHmac('sha1', key).update(base_string).digest('base64'); + } + }); + + //overide for testing only !!! + oauth.getTimeStamp = function() { + return 1484599369; + }; + + //overide for testing only !!! + oauth.getNonce = function(length) { + return 't62lMDp9DLwKZJJbZTpmSAhRINGBEOcF'; + }; + + var request = { + url: 'http://canvas.docker/api/lti/accounts/1/tool_proxy', + method: 'POST', + data: { + "@context":[ + "http://purl.imsglobal.org/ctx/lti/v2/ToolProxy" + ], + "@type":"ToolProxy", + "lti_version":"LTI-2p1", + "tool_proxy_guid":"0cf04d67-8a0d-4d41-af61-6e8c6fc3e68c", + "tool_consumer_profile":"http://canvas.docker/api/lti/accounts/1/tool_consumer_profile/339b6700-e4cb-47c5-a54f-3ee0064921a9", + "tool_profile":{ + "lti_version":"LTI-2p1", + "product_instance":{ + "guid":"fd75124a-140e-470f-944c-114d2d93db40", + "product_info":{ + "product_name":{ + "default_value":"TestTool", + "key":"tool.name" + }, + "product_version":"0.1.0", + "product_family":{ + "code":"testtool", + "vendor":{ + "code":"Example.com", + "vendor_name":{ + "default_value":"Example", + "key":"tool.vendor.name" + } + } + } + } + }, + "base_url_choice":[ + { + "default_base_url":"http://example.docker/", + "selector":{ + "applies_to":[ + "MessageHandler" + ] + } + } + ], + "resource_handler":[ + { + "resource_type":{ + "code":"testtool" + }, + "resource_name":{ + "default_value":"TestTool", + "key":"testtool.resource.name" + }, + "message":[ + { + "message_type":"basic-lti-launch-request", + "path":"lti_launch", + "enabled_capability":[ + "Canvas.placements.courseNavigation" + ] + } + ] + } + ] + }, + "enabled_capability":[ + "OAuth.splitSecret" + ], + "security_contract":{ + "tp_half_shared_secret":"1c7849d3c9f037a9891575c8508d3aaab6a9e1312b5d0353625f83d68f0d545344f81ff9e1849b6400982a0d3f6bf953c6095265e3b6d700a73f5be94ce5654c" + } + }, + includeBodyHash: true + }; + + describe('#getBodyHash', function() { + it('should handle data encoded as an object', function() { + expect(oauth.getBodyHash(request, '')).to.equal('F7L9O06JqL/LZpQBlsKC/7R53uM=') + }); + + it('should handle data encoded as a string', function() { + request.data = JSON.stringify(request.data) + expect(oauth.getBodyHash(request, '')).to.equal('F7L9O06JqL/LZpQBlsKC/7R53uM=') + }); + }); + + describe('#authorize', function() { + it('should properly include an oauth_body_hash param', function() { + expect(oauth.authorize(request)).to.eql({ + oauth_consumer_key: '1434affd-4d69-4a1a-bace-cc5c6fe493bc', + oauth_nonce: 't62lMDp9DLwKZJJbZTpmSAhRINGBEOcF', + oauth_signature_method: 'HMAC-SHA1', + oauth_timestamp: 1484599369, + oauth_version: '1.0', + oauth_body_hash: 'F7L9O06JqL/LZpQBlsKC/7R53uM=', + oauth_signature: 'ayrFZ4NkVhALlAPY8WjDJXuzK8Y=' + }); + }); + }); + + describe('#toHeader', function() { + it('should properly include an oauth_body_hash header', function() { + expect(oauth.toHeader(oauth.authorize(request))).to.have.property('Authorization', 'OAuth oauth_body_hash="F7L9O06JqL%2FLZpQBlsKC%2F7R53uM%3D", oauth_consumer_key="1434affd-4d69-4a1a-bace-cc5c6fe493bc", oauth_nonce="t62lMDp9DLwKZJJbZTpmSAhRINGBEOcF", oauth_signature="ayrFZ4NkVhALlAPY8WjDJXuzK8Y%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1484599369", oauth_version="1.0"'); + }); + }); +});