Skip to content

Commit

Permalink
Add body_hash_function for oauth_body_hash (#57)
Browse files Browse the repository at this point in the history
According to the [OAuth Request Body Hash draft], the oauth_body_hash algorithm for HMAC-SHA1 or RSA-SHA1 Oauth signature methods should be SHA1. This patch keep using the default hash function for the body hash but allows to define an alternative one via the `body_hash_function` initial option.

[OAuth Request Body Hash draft]: https://tools.ietf.org/id/draft-eaton-oauth-bodyhash-00.html#anchor3
  • Loading branch information
dinoboff authored and ddo committed Dec 18, 2017
1 parent 13a92ec commit 47b8413
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 92 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,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
* includeBodyHash: ``Boolean`` default ``false`` set to true if you want ``oauth_body_hash`` signing (you will need to have define the ``body_hash_function`` in most cases - for HMAC-SHA1 Oauth signature method, the ``body_hash_function`` should return a SHA1 hash).

```js
const request_data = {
Expand Down Expand Up @@ -284,6 +284,7 @@ const oauth = OAuth(/* options */);

* ``signature_method``: ``String`` default ``'PLAINTEXT'``
* ``hash_function``: ``Function`` if ``signature_method`` = ``'PLAINTEXT'`` default ``return key``
* ``body_hash_function``: ``Function`` default to ``hash_function``
* ``nonce_length``: ``Int`` default ``32``
* ``version``: ``String`` default ``'1.0'``
* ``parameter_seperator``: ``String`` for header only, default ``', '``. Note that there is a space after ``,``
Expand Down
7 changes: 6 additions & 1 deletion oauth-1.0a.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function OAuth(opts) {
}

this.hash_function = opts.hash_function;
this.body_hash_function = opts.body_hash_function || this.hash_function;
}

/**
Expand Down Expand Up @@ -106,7 +107,11 @@ OAuth.prototype.getSignature = function(request, token_secret, oauth_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))
if (!this.body_hash_function) {
throw new Error('body_hash_function option is required');
}

return this.body_hash_function(body, this.getSigningKey(token_secret))
};

/**
Expand Down
190 changes: 100 additions & 90 deletions test/oauth_body_hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,109 +3,119 @@ 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');
}
});
var oauth, request;

//overide for testing only !!!
oauth.getTimeStamp = function() {
return 1484599369;
};
beforeEach(function() {
var algo = 'sha1';
var enc = 'base64';

//overide for testing only !!!
oauth.getNonce = function(length) {
return 't62lMDp9DLwKZJJbZTpmSAhRINGBEOcF';
};
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(algo, key).update(base_string).digest(enc);
},
body_hash_function: function (data) {
return crypto.createHash(algo).update(data).digest(enc);
}
});

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"
//overide for testing only !!!
oauth.getTimeStamp = function () {
return 1484599369;
};

//overide for testing only !!!
oauth.getNonce = function (length) {
return 't62lMDp9DLwKZJJbZTpmSAhRINGBEOcF';
};

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"
},
"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"
],
"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"
]
}
]
}
]
"security_contract": {
"tp_half_shared_secret": "1c7849d3c9f037a9891575c8508d3aaab6a9e1312b5d0353625f83d68f0d545344f81ff9e1849b6400982a0d3f6bf953c6095265e3b6d700a73f5be94ce5654c"
}
},
"enabled_capability":[
"OAuth.splitSecret"
],
"security_contract":{
"tp_half_shared_secret":"1c7849d3c9f037a9891575c8508d3aaab6a9e1312b5d0353625f83d68f0d545344f81ff9e1849b6400982a0d3f6bf953c6095265e3b6d700a73f5be94ce5654c"
}
},
includeBodyHash: true
};
includeBodyHash: true
};
});

describe('#getBodyHash', function() {
it('should handle data encoded as an object', function() {
expect(oauth.getBodyHash(request, '')).to.equal('F7L9O06JqL/LZpQBlsKC/7R53uM=')
expect(oauth.getBodyHash(request, '')).to.equal('xpJzRG6xylVIRRtiigLPKX7iRmM=')
});

it('should handle data encoded as a string', function() {
request.data = JSON.stringify(request.data)
expect(oauth.getBodyHash(request, '')).to.equal('F7L9O06JqL/LZpQBlsKC/7R53uM=')
request.data = 'Hello World!';
expect(oauth.getBodyHash(request, '')).to.equal('Lve95gjOVATpfV8EL5X4nxwjKHE=')
});
});

Expand All @@ -117,15 +127,15 @@ describe("OAuth Body Hash", function() {
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: 1484599369,
oauth_version: '1.0',
oauth_body_hash: 'F7L9O06JqL/LZpQBlsKC/7R53uM=',
oauth_signature: 'ayrFZ4NkVhALlAPY8WjDJXuzK8Y='
oauth_body_hash: 'xpJzRG6xylVIRRtiigLPKX7iRmM=',
oauth_signature: '1Q0U8yhK1bWYguRxlUDs9KHywOE='
});
});
});

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"');
expect(oauth.toHeader(oauth.authorize(request))).to.have.property('Authorization', 'OAuth oauth_body_hash="xpJzRG6xylVIRRtiigLPKX7iRmM%3D", oauth_consumer_key="1434affd-4d69-4a1a-bace-cc5c6fe493bc", oauth_nonce="t62lMDp9DLwKZJJbZTpmSAhRINGBEOcF", oauth_signature="1Q0U8yhK1bWYguRxlUDs9KHywOE%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1484599369", oauth_version="1.0"');
});
});
});

0 comments on commit 47b8413

Please sign in to comment.