Merge remote-tracking branch 'origin/master' into evanp-issue144

This commit is contained in:
ciaranj 2014-05-21 20:48:58 +01:00
commit 4e351fef16
6 changed files with 384 additions and 82 deletions

View File

@ -6,6 +6,8 @@ Tested against Twitter (http://twitter.com), term.ie (http://term.ie/oauth/examp
Also provides rudimentary OAuth2 support, tested against facebook, github, foursquare, google and Janrain. For more complete usage examples please take a look at connect-auth (http://github.com/ciaranj/connect-auth) Also provides rudimentary OAuth2 support, tested against facebook, github, foursquare, google and Janrain. For more complete usage examples please take a look at connect-auth (http://github.com/ciaranj/connect-auth)
[![Clone in Koding](http://learn.koding.com/btn/clone_d.png)][koding]
[koding]: https://koding.com/Teamwork?import=https://github.com/ciaranj/node-oauth/archive/master.zip&c=git1
Installation Installation
============== ==============
@ -22,7 +24,7 @@ To run examples/tests insall Mocha `$ npm install -g mocha` and run `$ mocha you
```javascript ```javascript
describe('OAuth1.0',function(){ describe('OAuth1.0',function(){
var OAuth = require('OAuth'); var OAuth = require('oauth');
it('tests trends Twitter API v1.1',function(done){ it('tests trends Twitter API v1.1',function(done){
var oauth = new OAuth.OAuth( var oauth = new OAuth.OAuth(
@ -36,7 +38,7 @@ describe('OAuth1.0',function(){
); );
oauth.get( oauth.get(
'https://api.twitter.com/1.1/trends/place.json?id=23424977', 'https://api.twitter.com/1.1/trends/place.json?id=23424977',
'your user toke for this app', //test user token 'your user token for this app', //test user token
'your user secret for this app', //test user secret 'your user secret for this app', //test user secret
function (e, data, res){ function (e, data, res){
if (e) console.error(e); if (e) console.error(e);
@ -50,7 +52,7 @@ describe('OAuth1.0',function(){
## OAuth2.0 ## OAuth2.0
```javascript ```javascript
describe('OAuth2',function(){ describe('OAuth2',function(){
var OAuth = require('OAuth'); var OAuth = require('oauth');
it('gets bearer token', function(done){ it('gets bearer token', function(done){
var OAuth2 = OAuth.OAuth2; var OAuth2 = OAuth.OAuth2;
@ -75,6 +77,11 @@ describe('OAuth2',function(){
Change History Change History
============== ==============
* 0.9.11
- OAuth2: No longer sends the type=webserver argument with the OAuth2 requests (thank you bendiy)
- OAuth2: Provides a default (and overrideable) User-Agent header (thanks to Andrew Martens & Daniel Mahlow)
- OAuth1: New followRedirects client option (true by default) (thanks to Pieter Joost van de Sande)
- OAuth1: Adds RSA-SHA1 support (thanks to Jeffrey D. Van Alstine & Michael Garvin & Andreas Knecht)
* 0.9.10 * 0.9.10
- OAuth2: Addresses 2 issues that came in with 0.9.9, #129 & #125 (thank you José F. Romaniello) - OAuth2: Addresses 2 issues that came in with 0.9.9, #129 & #125 (thank you José F. Romaniello)
* 0.9.9 * 0.9.9
@ -156,3 +163,10 @@ Contributors (In no particular order)
* rolandboon - http://rolandboon.com * rolandboon - http://rolandboon.com
* Brian Park - http://github.com/yaru22 * Brian Park - http://github.com/yaru22
* José F. Romaniello - http://github.com/jfromaniello * José F. Romaniello - http://github.com/jfromaniello
* bendiy - https://github.com/bendiy
* Andrew Martins - http://www.andrewmartens.com
* Daniel Mahlow - https://github.com/dmahlow
* Pieter Joost van de Sande - https://github.com/pjvds
* Jeffrey D. Van Alstine
* Michael Garvin
* Andreas Knecht

View File

@ -13,6 +13,9 @@ exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, vers
this._accessUrl= accessUrl; this._accessUrl= accessUrl;
this._consumerKey= consumerKey; this._consumerKey= consumerKey;
this._consumerSecret= this._encodeData( consumerSecret ); this._consumerSecret= this._encodeData( consumerSecret );
if (signatureMethod == "RSA-SHA1") {
this._privateKey = consumerSecret;
}
this._version= version; this._version= version;
if( authorize_callback === undefined ) { if( authorize_callback === undefined ) {
this._authorize_callback= "oob"; this._authorize_callback= "oob";
@ -21,7 +24,7 @@ exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, vers
this._authorize_callback= authorize_callback; this._authorize_callback= authorize_callback;
} }
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1") if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-SHA1")
throw new Error("Un-supported signature method: " + signatureMethod ) throw new Error("Un-supported signature method: " + signatureMethod )
this._signatureMethod= signatureMethod; this._signatureMethod= signatureMethod;
this._nonceSize= nonceSize || 32; this._nonceSize= nonceSize || 32;
@ -29,7 +32,8 @@ exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, vers
"Connection" : "close", "Connection" : "close",
"User-Agent" : "Node authentication"} "User-Agent" : "Node authentication"}
this._clientOptions= this._defaultClientOptions= {"requestTokenHttpMethod": "POST", this._clientOptions= this._defaultClientOptions= {"requestTokenHttpMethod": "POST",
"accessTokenHttpMethod": "POST"}; "accessTokenHttpMethod": "POST",
"followRedirects": true};
this._oauthParameterSeperator = ","; this._oauthParameterSeperator = ",";
}; };
@ -40,9 +44,12 @@ exports.OAuthEcho= function(realm, verify_credentials, consumerKey, consumerSecr
this._verifyCredentials = verify_credentials; this._verifyCredentials = verify_credentials;
this._consumerKey= consumerKey; this._consumerKey= consumerKey;
this._consumerSecret= this._encodeData( consumerSecret ); this._consumerSecret= this._encodeData( consumerSecret );
if (signatureMethod == "RSA-SHA1") {
this._privateKey = consumerSecret;
}
this._version= version; this._version= version;
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1") if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-SHA1")
throw new Error("Un-supported signature method: " + signatureMethod ); throw new Error("Un-supported signature method: " + signatureMethod );
this._signatureMethod= signatureMethod; this._signatureMethod= signatureMethod;
this._nonceSize= nonceSize || 32; this._nonceSize= nonceSize || 32;
@ -86,7 +93,7 @@ exports.OAuth.prototype._getSignature= function(method, url, parameters, tokenSe
exports.OAuth.prototype._normalizeUrl= function(url) { exports.OAuth.prototype._normalizeUrl= function(url) {
var parsedUrl= URL.parse(url, true) var parsedUrl= URL.parse(url, true)
var port =""; var port ="";
if( parsedUrl.port ) { if( parsedUrl.port ) {
if( (parsedUrl.protocol == "http:" && parsedUrl.port != "80" ) || if( (parsedUrl.protocol == "http:" && parsedUrl.port != "80" ) ||
(parsedUrl.protocol == "https:" && parsedUrl.port != "443") ) { (parsedUrl.protocol == "https:" && parsedUrl.port != "443") ) {
port= ":" + parsedUrl.port; port= ":" + parsedUrl.port;
@ -94,7 +101,7 @@ exports.OAuth.prototype._normalizeUrl= function(url) {
} }
if( !parsedUrl.pathname || parsedUrl.pathname == "" ) parsedUrl.pathname ="/"; if( !parsedUrl.pathname || parsedUrl.pathname == "" ) parsedUrl.pathname ="/";
return parsedUrl.protocol + "//" + parsedUrl.hostname + port + parsedUrl.pathname; return parsedUrl.protocol + "//" + parsedUrl.hostname + port + parsedUrl.pathname;
} }
@ -124,7 +131,7 @@ exports.OAuth.prototype._buildAuthorizationHeaders= function(orderedParameters)
} }
} }
authHeader= authHeader.substring(0, authHeader.length-this._oauthParameterSeperator.length); authHeader= authHeader.substring(0, authHeader.length-this._oauthParameterSeperator.length);
return authHeader; return authHeader;
} }
@ -143,33 +150,33 @@ exports.OAuth.prototype._makeArrayOfArgumentsHash= function(argumentsHash) {
argument_pairs[argument_pairs.length]= [key, value]; argument_pairs[argument_pairs.length]= [key, value];
} }
} }
return argument_pairs; return argument_pairs;
} }
// Sorts the encoded key value pairs by encoded name, then encoded value // Sorts the encoded key value pairs by encoded name, then encoded value
exports.OAuth.prototype._sortRequestParams= function(argument_pairs) { exports.OAuth.prototype._sortRequestParams= function(argument_pairs) {
// Sort by name, then value. // Sort by name, then value.
argument_pairs.sort(function(a,b) { argument_pairs.sort(function(a,b) {
if ( a[0]== b[0] ) { if ( a[0]== b[0] ) {
return a[1] < b[1] ? -1 : 1; return a[1] < b[1] ? -1 : 1;
} }
else return a[0] < b[0] ? -1 : 1; else return a[0] < b[0] ? -1 : 1;
}); });
return argument_pairs; return argument_pairs;
} }
exports.OAuth.prototype._normaliseRequestParams= function(arguments) { exports.OAuth.prototype._normaliseRequestParams= function(args) {
var argument_pairs= this._makeArrayOfArgumentsHash(arguments); var argument_pairs= this._makeArrayOfArgumentsHash(args);
// First encode them #3.4.1.3.2 .1 // First encode them #3.4.1.3.2 .1
for(var i=0;i<argument_pairs.length;i++) { for(var i=0;i<argument_pairs.length;i++) {
argument_pairs[i][0]= this._encodeData( argument_pairs[i][0] ); argument_pairs[i][0]= this._encodeData( argument_pairs[i][0] );
argument_pairs[i][1]= this._encodeData( argument_pairs[i][1] ); argument_pairs[i][1]= this._encodeData( argument_pairs[i][1] );
} }
// Then sort them #3.4.1.3.2 .2 // Then sort them #3.4.1.3.2 .2
argument_pairs= this._sortRequestParams( argument_pairs ); argument_pairs= this._sortRequestParams( argument_pairs );
// Then concatenate together #3.4.1.3.2 .3 & .4 // Then concatenate together #3.4.1.3.2 .3 & .4
var args= ""; var args= "";
for(var i=0;i<argument_pairs.length;i++) { for(var i=0;i<argument_pairs.length;i++) {
@ -177,19 +184,19 @@ exports.OAuth.prototype._normaliseRequestParams= function(arguments) {
args+= "=" args+= "="
args+= argument_pairs[i][1]; args+= argument_pairs[i][1];
if( i < argument_pairs.length-1 ) args+= "&"; if( i < argument_pairs.length-1 ) args+= "&";
} }
return args; return args;
} }
exports.OAuth.prototype._createSignatureBase= function(method, url, parameters) { exports.OAuth.prototype._createSignatureBase= function(method, url, parameters) {
url= this._encodeData( this._normalizeUrl(url) ); url= this._encodeData( this._normalizeUrl(url) );
parameters= this._encodeData( parameters ); parameters= this._encodeData( parameters );
return method.toUpperCase() + "&" + url + "&" + parameters; return method.toUpperCase() + "&" + url + "&" + parameters;
} }
exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) { exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) {
if( tokenSecret === undefined ) var tokenSecret= ""; if( tokenSecret === undefined ) var tokenSecret= "";
else tokenSecret= this._encodeData( tokenSecret ); else tokenSecret= this._encodeData( tokenSecret );
// consumerSecret is already encoded // consumerSecret is already encoded
var key= this._consumerSecret + "&" + tokenSecret; var key= this._consumerSecret + "&" + tokenSecret;
@ -197,12 +204,16 @@ exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) {
if( this._signatureMethod == "PLAINTEXT" ) { if( this._signatureMethod == "PLAINTEXT" ) {
hash= key; hash= key;
} }
else if (this._signatureMethod == "RSA-SHA1") {
key = this._privateKey || "";
hash= crypto.createSign("RSA-SHA1").update(signatureBase).sign(key, 'base64');
}
else { else {
if( crypto.Hmac ) { if( crypto.Hmac ) {
hash = crypto.createHmac("sha1", key).update(signatureBase).digest("base64"); hash = crypto.createHmac("sha1", key).update(signatureBase).digest("base64");
} }
else { else {
hash= sha1.HMACSHA1(key, signatureBase); hash= sha1.HMACSHA1(key, signatureBase);
} }
} }
return hash; return hash;
@ -218,7 +229,7 @@ exports.OAuth.prototype._getNonce= function(nonceSize) {
var chars= this.NONCE_CHARS; var chars= this.NONCE_CHARS;
var char_pos; var char_pos;
var nonce_chars_length= chars.length; var nonce_chars_length= chars.length;
for (var i = 0; i < nonceSize; i++) { for (var i = 0; i < nonceSize; i++) {
char_pos= Math.floor(Math.random() * nonce_chars_length); char_pos= Math.floor(Math.random() * nonce_chars_length);
result[i]= chars[char_pos]; result[i]= chars[char_pos];
@ -240,7 +251,7 @@ exports.OAuth.prototype._createClient= function( port, hostname, method, path, h
} else { } else {
httpModel= http; httpModel= http;
} }
return httpModel.request(options); return httpModel.request(options);
} }
exports.OAuth.prototype._prepareParameters= function( oauth_token, oauth_token_secret, method, url, extra_params ) { exports.OAuth.prototype._prepareParameters= function( oauth_token, oauth_token_secret, method, url, extra_params ) {
@ -361,22 +372,23 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
request= this._createClient(parsedUrl.port, parsedUrl.hostname, method, path, headers); request= this._createClient(parsedUrl.port, parsedUrl.hostname, method, path, headers);
} }
var clientOptions = this._clientOptions;
if( callback ) { if( callback ) {
var data=""; var data="";
var self= this; var self= this;
// Some hosts *cough* google appear to close the connection early / send no content-length header // Some hosts *cough* google appear to close the connection early / send no content-length header
// allow this behaviour. // allow this behaviour.
var allowEarlyClose= OAuthUtils.isAnEarlyCloseHost( parsedUrl.hostname ); var allowEarlyClose= OAuthUtils.isAnEarlyCloseHost( parsedUrl.hostname );
var callbackCalled= false; var callbackCalled= false;
function passBackControl( response ) { var passBackControl = function( response ) {
if(!callbackCalled) { if(!callbackCalled) {
callbackCalled= true; callbackCalled= true;
if ( response.statusCode >= 200 && response.statusCode <= 299 ) { if ( response.statusCode >= 200 && response.statusCode <= 299 ) {
callback(null, data, response); callback(null, data, response);
} else { } else {
// Follow 301 or 302 redirects with Location HTTP header // Follow 301 or 302 redirects with Location HTTP header
if((response.statusCode == 301 || response.statusCode == 302) && response.headers && response.headers.location) { if((response.statusCode == 301 || response.statusCode == 302) && clientOptions.followRedirects && response.headers && response.headers.location) {
self._performSecureRequest( oauth_token, oauth_token_secret, method, response.headers.location, extra_params, post_body, post_content_type, callback); self._performSecureRequest( oauth_token, oauth_token_secret, method, response.headers.location, extra_params, post_body, post_content_type, callback);
} }
else { else {
@ -400,12 +412,14 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
} }
}); });
}); });
request.on("error", function(err) { request.on("error", function(err) {
callbackCalled= true; if(!callbackCalled) {
callback( err ) callbackCalled= true;
callback( err )
}
}); });
if( (method == "POST" || method =="PUT") && post_body != null && post_body != "" ) { if( (method == "POST" || method =="PUT") && post_body != null && post_body != "" ) {
request.write(post_body); request.write(post_body);
} }
@ -417,7 +431,7 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
} }
return request; return request;
} }
return; return;
} }
@ -444,7 +458,7 @@ exports.OAuth.prototype.getOAuthAccessToken= function(oauth_token, oauth_token_s
} else { } else {
extraParams.oauth_verifier= oauth_verifier; extraParams.oauth_verifier= oauth_verifier;
} }
this._performSecureRequest( oauth_token, oauth_token_secret, this._clientOptions.accessTokenHttpMethod, this._accessUrl, extraParams, null, null, function(error, data, response) { this._performSecureRequest( oauth_token, oauth_token_secret, this._clientOptions.accessTokenHttpMethod, this._accessUrl, extraParams, null, null, function(error, data, response) {
if( error ) callback(error); if( error ) callback(error);
else { else {
@ -484,7 +498,7 @@ exports.OAuth.prototype._putOrPost= function(method, url, oauth_token, oauth_tok
} }
return this._performSecureRequest( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ); return this._performSecureRequest( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback );
} }
exports.OAuth.prototype.put= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) { exports.OAuth.prototype.put= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) {
return this._putOrPost("PUT", url, oauth_token, oauth_token_secret, post_body, post_content_type, callback); return this._putOrPost("PUT", url, oauth_token, oauth_token_secret, post_body, post_content_type, callback);
@ -500,7 +514,7 @@ exports.OAuth.prototype.post= function(url, oauth_token, oauth_token_secret, pos
* *
* The callback should expect a function of the following form: * The callback should expect a function of the following form:
* *
* function(err, token, token_secret, parsedQueryString) {} * function(err, token, token_secret, parsedQueryString) {}
* *
* This method has optional parameters so can be called in the following 2 ways: * This method has optional parameters so can be called in the following 2 ways:
* *
@ -519,7 +533,7 @@ exports.OAuth.prototype.getOAuthRequestToken= function( extraParams, callback )
callback = extraParams; callback = extraParams;
extraParams = {}; extraParams = {};
} }
// Callbacks are 1.0A related // Callbacks are 1.0A related
if( this._authorize_callback ) { if( this._authorize_callback ) {
extraParams["oauth_callback"]= this._authorize_callback; extraParams["oauth_callback"]= this._authorize_callback;
} }
@ -546,12 +560,12 @@ exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret,
var orderedParameters= this._prepareParameters(oauth_token, oauth_token_secret, method, url, {}); var orderedParameters= this._prepareParameters(oauth_token, oauth_token_secret, method, url, {});
var parsedUrl= URL.parse( url, false ); var parsedUrl= URL.parse( url, false );
var query=""; var query="";
for( var i= 0 ; i < orderedParameters.length; i++) { for( var i= 0 ; i < orderedParameters.length; i++) {
query+= orderedParameters[i][0]+"="+ this._encodeData(orderedParameters[i][1]) + "&"; query+= orderedParameters[i][0]+"="+ this._encodeData(orderedParameters[i][1]) + "&";
} }
query= query.substring(0, query.length-1); query= query.substring(0, query.length-1);
return parsedUrl.protocol + "//"+ parsedUrl.host + parsedUrl.pathname + "?" + query; return parsedUrl.protocol + "//"+ parsedUrl.host + parsedUrl.pathname + "?" + query;
}; };

View File

@ -49,19 +49,25 @@ exports.OAuth2.prototype.buildAuthHeader= function(token) {
return this._authMethod + ' ' + token; return this._authMethod + ' ' + token;
}; };
exports.OAuth2.prototype._chooseHttpLibrary= function( parsedUrl ) {
var http_library= https;
// As this is OAUth2, we *assume* https unless told explicitly otherwise.
if( parsedUrl.protocol != "https:" ) {
http_library= http;
}
return http_library;
};
exports.OAuth2.prototype._request= function(method, url, headers, post_body, access_token, callback) { exports.OAuth2.prototype._request= function(method, url, headers, post_body, access_token, callback) {
var http_library= https;
var creds = crypto.createCredentials({ }); var creds = crypto.createCredentials({ });
var parsedUrl= URL.parse( url, true ); var parsedUrl= URL.parse( url, true );
if( parsedUrl.protocol == "https:" && !parsedUrl.port ) { if( parsedUrl.protocol == "https:" && !parsedUrl.port ) {
parsedUrl.port= 443; parsedUrl.port= 443;
} }
// As this is OAUth2, we *assume* https unless told explicitly otherwise. var http_library= this._chooseHttpLibrary( parsedUrl );
if( parsedUrl.protocol != "https:" ) {
http_library= http;
}
var realHeaders= {}; var realHeaders= {};
for( var key in this._customHeaders ) { for( var key in this._customHeaders ) {
@ -74,6 +80,10 @@ exports.OAuth2.prototype._request= function(method, url, headers, post_body, acc
} }
realHeaders['Host']= parsedUrl.host; realHeaders['Host']= parsedUrl.host;
if (!realHeaders['User-Agent']) {
realHeaders['User-Agent'] = 'Node-oauth';
}
realHeaders['Content-Length']= post_body ? Buffer.byteLength(post_body) : 0; realHeaders['Content-Length']= post_body ? Buffer.byteLength(post_body) : 0;
if( access_token && !('Authorization' in realHeaders)) { if( access_token && !('Authorization' in realHeaders)) {
if( ! parsedUrl.query ) parsedUrl.query= {}; if( ! parsedUrl.query ) parsedUrl.query= {};
@ -129,16 +139,15 @@ exports.OAuth2.prototype._executeRequest= function( http_library, options, post_
callback(e); callback(e);
}); });
if( options.method == 'POST' && post_body ) { if( (options.method == 'POST' || options.method == 'PUT') && post_body ) {
request.write(post_body); request.write(post_body);
} }
request.end(); request.end();
} }
exports.OAuth2.prototype.getAuthorizeUrl= function( params ) { exports.OAuth2.prototype.getAuthorizeUrl= function( params ) {
var params= params || {}; var params= params || {};
params['client_id'] = this._clientId; params['client_id'] = this._clientId;
params['type'] = 'web_server';
return this._baseSite + this._authorizeUrl + "?" + querystring.stringify(params); return this._baseSite + this._authorizeUrl + "?" + querystring.stringify(params);
} }
@ -146,7 +155,6 @@ exports.OAuth2.prototype.getOAuthAccessToken= function(code, params, callback) {
var params= params || {}; var params= params || {};
params['client_id'] = this._clientId; params['client_id'] = this._clientId;
params['client_secret'] = this._clientSecret; params['client_secret'] = this._clientSecret;
params['type']= 'web_server';
var codeParam = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code'; var codeParam = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code';
params[codeParam]= code; params[codeParam]= code;

View File

@ -1,6 +1,6 @@
{ "name" : "oauth" { "name" : "oauth"
, "description" : "Library for interacting with OAuth 1.0, 1.0A, 2 and Echo. Provides simplified client access and allows for construction of more complex apis and OAuth providers." , "description" : "Library for interacting with OAuth 1.0, 1.0A, 2 and Echo. Provides simplified client access and allows for construction of more complex apis and OAuth providers."
, "version" : "0.9.10" , "version" : "0.9.11"
, "directories" : { "lib" : "./lib" } , "directories" : { "lib" : "./lib" }
, "main" : "index.js" , "main" : "index.js"
, "author" : "Ciaran Jessup <ciaranj@gmail.com>" , "author" : "Ciaran Jessup <ciaranj@gmail.com>"

View File

@ -2,7 +2,8 @@ var vows = require('vows'),
assert = require('assert'), assert = require('assert'),
events = require('events'), events = require('events'),
OAuth= require('../lib/oauth').OAuth, OAuth= require('../lib/oauth').OAuth,
OAuthEcho= require('../lib/oauth').OAuthEcho; OAuthEcho= require('../lib/oauth').OAuthEcho,
crypto = require('crypto');
var DummyResponse =function( statusCode ) { var DummyResponse =function( statusCode ) {
this.statusCode= statusCode; this.statusCode= statusCode;
@ -21,21 +22,65 @@ DummyRequest.prototype.write= function(post_body){
} }
DummyRequest.prototype.end= function(){ DummyRequest.prototype.end= function(){
this.response.emit('end'); this.response.emit('end');
} }
//Valid RSA keypair used to test RSA-SHA1 signature method
var RsaPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIICXQIBAAKBgQDizE4gQP5nPQhzof/Vp2U2DDY3UY/Gxha2CwKW0URe7McxtnmE\n" +
"CrZnT1n/YtfrrCNxY5KMP4o8hMrxsYEe05+1ZGFT68ztms3puUxilU5E3BQMhz1t\n" +
"JMJEGcTt8nZUlM4utli7fHgDtWbhvqvYjRMGn3AjyLOfY8XZvnFkGjipvQIDAQAB\n" +
"AoGAKgk6FcpWHOZ4EY6eL4iGPt1Gkzw/zNTcUsN5qGCDLqDuTq2Gmk2t/zn68VXt\n" +
"tVXDf/m3qN0CDzOBtghzaTZKLGhnSewQ98obMWgPcvAsb4adEEeW1/xigbMiaW2X\n" +
"cu6GhZxY16edbuQ40LRrPoVK94nXQpj8p7w4IQ301Sm8PSECQQD1ZlOj4ugvfhEt\n" +
"exi4WyAaM45fylmN290UXYqZ8SYPI/VliDytIlMfyq5Rv+l+dud1XDPrWOQ0ImgV\n" +
"HJn7uvoZAkEA7JhHNmHF9dbdF9Koj86K2Cl6c8KUu7U7d2BAuB6pPkt8+D8+y4St\n" +
"PaCmN4oP4X+sf5rqBYoXywHlqEei2BdpRQJBAMYgR4cZu7wcXGIL8HlnmROObHSK\n" +
"OqN9z5CRtUV0nPW8YnQG+nYOMG6KhRMbjri750OpnYF100kEPmRNI0VKQIECQE8R\n" +
"fQsRleTYz768ahTVQ9WF1ySErMwmfx8gDcD6jjkBZVxZVpURXAwyehopi7Eix/VF\n" +
"QlxjkBwKIEQi3Ks297kCQQCL9by1bueKDMJO2YX1Brm767pkDKkWtGfPS+d3xMtC\n" +
"KJHHCqrS1V+D5Q89x5wIRHKxE5UMTc0JNa554OxwFORX\n" +
"-----END RSA PRIVATE KEY-----";
var RsaPublicKey = "-----BEGIN PUBLIC KEY-----\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDizE4gQP5nPQhzof/Vp2U2DDY3\n" +
"UY/Gxha2CwKW0URe7McxtnmECrZnT1n/YtfrrCNxY5KMP4o8hMrxsYEe05+1ZGFT\n" +
"68ztms3puUxilU5E3BQMhz1tJMJEGcTt8nZUlM4utli7fHgDtWbhvqvYjRMGn3Aj\n" +
"yLOfY8XZvnFkGjipvQIDAQAB\n" +
"-----END PUBLIC KEY-----";
vows.describe('OAuth').addBatch({ vows.describe('OAuth').addBatch({
'When newing OAuth': {
topic: new OAuth(null, null, null, null, null, null, "PLAINTEXT"),
'followRedirects is enabled by default': function (oa) {
assert.equal(oa._clientOptions.followRedirects, true)
}
},
'When generating the signature base string described in http://oauth.net/core/1.0/#sig_base_example': { 'When generating the signature base string described in http://oauth.net/core/1.0/#sig_base_example': {
topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"),
'we get the expected result string': function (oa) { 'we get the expected result string': function (oa) {
var result= oa._createSignatureBase("GET", "http://photos.example.net/photos", var result= oa._createSignatureBase("GET", "http://photos.example.net/photos",
"file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original") "file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original")
assert.equal( result, "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal"); assert.equal( result, "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal");
} }
}, },
'When generating the signature with RSA-SHA1': {
topic: new OAuth(null, null, null, RsaPrivateKey, null, null, "RSA-SHA1"),
'we get a valid oauth signature': function (oa) {
var signatureBase = "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal";
var oauthSignature = oa._createSignature(signatureBase, "xyz4992k83j47x0b");
assert.equal( oauthSignature, "qS4rhWog7GPgo4ZCJvUdC/1ZAax/Q4Ab9yOBvgxSopvmKUKp5rso+Zda46GbyN2hnYDTiA/g3P/d/YiPWa454BEBb/KWFV83HpLDIoqUUhJnlXX9MqRQQac0oeope4fWbGlfTdL2PXjSFJmvfrzybERD/ZufsFtVrQKS3QBpYiw=");
//now check that given the public key we can verify this signature
var verifier = crypto.createVerify("RSA-SHA1").update(signatureBase);
var valid = verifier.verify(RsaPublicKey, oauthSignature, 'base64');
assert.ok( valid, "Signature could not be verified with RSA public key");
}
},
'When generating the signature base string with PLAINTEXT': { 'When generating the signature base string with PLAINTEXT': {
topic: new OAuth(null, null, null, null, null, null, "PLAINTEXT"), topic: new OAuth(null, null, null, null, null, null, "PLAINTEXT"),
'we get the expected result string': function (oa) { 'we get the expected result string': function (oa) {
var result= oa._getSignature("GET", "http://photos.example.net/photos", var result= oa._getSignature("GET", "http://photos.example.net/photos",
"file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=PLAINTEXT&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original", "file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=PLAINTEXT&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original",
"test"); "test");
assert.equal( result, "&test"); assert.equal( result, "&test");
@ -58,7 +103,7 @@ vows.describe('OAuth').addBatch({
topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"),
'flatten out arguments that are arrays' : function(oa) { 'flatten out arguments that are arrays' : function(oa) {
var parameters= {"z": "a", var parameters= {"z": "a",
"a": ["1", "2"], "a": ["1", "2"],
"1": "c" }; "1": "c" };
var parameterResults= oa._makeArrayOfArgumentsHash(parameters); var parameterResults= oa._makeArrayOfArgumentsHash(parameters);
assert.equal(parameterResults.length, 4); assert.equal(parameterResults.length, 4);
@ -72,30 +117,30 @@ vows.describe('OAuth').addBatch({
topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"),
'Order them by name' : function(oa) { 'Order them by name' : function(oa) {
var parameters= {"z": "a", var parameters= {"z": "a",
"a": "b", "a": "b",
"1": "c" }; "1": "c" };
var parameterResults= oa._sortRequestParams(oa._makeArrayOfArgumentsHash(parameters)) var parameterResults= oa._sortRequestParams(oa._makeArrayOfArgumentsHash(parameters))
assert.equal(parameterResults[0][0], "1"); assert.equal(parameterResults[0][0], "1");
assert.equal(parameterResults[1][0], "a"); assert.equal(parameterResults[1][0], "a");
assert.equal(parameterResults[2][0], "z"); assert.equal(parameterResults[2][0], "z");
}, },
'If two parameter names are the same then order by the value': function(oa) { 'If two parameter names are the same then order by the value': function(oa) {
var parameters= {"z": "a", var parameters= {"z": "a",
"a": ["z", "b", "b", "a", "y"], "a": ["z", "b", "b", "a", "y"],
"1": "c" }; "1": "c" };
var parameterResults= oa._sortRequestParams(oa._makeArrayOfArgumentsHash(parameters)) var parameterResults= oa._sortRequestParams(oa._makeArrayOfArgumentsHash(parameters))
assert.equal(parameterResults[0][0], "1"); assert.equal(parameterResults[0][0], "1");
assert.equal(parameterResults[1][0], "a"); assert.equal(parameterResults[1][0], "a");
assert.equal(parameterResults[1][1], "a"); assert.equal(parameterResults[1][1], "a");
assert.equal(parameterResults[2][0], "a"); assert.equal(parameterResults[2][0], "a");
assert.equal(parameterResults[2][1], "b"); assert.equal(parameterResults[2][1], "b");
assert.equal(parameterResults[3][0], "a"); assert.equal(parameterResults[3][0], "a");
assert.equal(parameterResults[3][1], "b"); assert.equal(parameterResults[3][1], "b");
assert.equal(parameterResults[4][0], "a"); assert.equal(parameterResults[4][0], "a");
assert.equal(parameterResults[4][1], "y"); assert.equal(parameterResults[4][1], "y");
assert.equal(parameterResults[5][0], "a"); assert.equal(parameterResults[5][0], "a");
assert.equal(parameterResults[5][1], "z"); assert.equal(parameterResults[5][1], "z");
assert.equal(parameterResults[6][0], "z"); assert.equal(parameterResults[6][0], "z");
} }
}, },
'When normalising the request parameters': { 'When normalising the request parameters': {
@ -193,7 +238,7 @@ vows.describe('OAuth').addBatch({
'Support variable whitespace separating the arguments': function(oa) { 'Support variable whitespace separating the arguments': function(oa) {
oa._oauthParameterSeperator= ", "; oa._oauthParameterSeperator= ", ";
assert.equal( oa.authHeader("http://somehost.com:3323/foo/poop?bar=foo", "token", "tokensecret"), 'OAuth oauth_consumer_key="consumerkey", oauth_nonce="ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1272399856", oauth_token="token", oauth_version="1.0", oauth_signature="zeOR0Wsm6EG6XSg0Vw%2FsbpoSib8%3D"'); assert.equal( oa.authHeader("http://somehost.com:3323/foo/poop?bar=foo", "token", "tokensecret"), 'OAuth oauth_consumer_key="consumerkey", oauth_nonce="ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1272399856", oauth_token="token", oauth_version="1.0", oauth_signature="zeOR0Wsm6EG6XSg0Vw%2FsbpoSib8%3D"');
} }
}, },
'When get the OAuth Echo authorization header': { 'When get the OAuth Echo authorization header': {
topic: function () { topic: function () {
@ -229,7 +274,7 @@ vows.describe('OAuth').addBatch({
} }
}, },
'When building the OAuth Authorization header': { 'When building the OAuth Authorization header': {
topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), topic: new OAuth(null, null, null, null, null, null, "HMAC-SHA1"),
'All provided oauth arguments should be concatentated correctly' : function(oa) { 'All provided oauth arguments should be concatentated correctly' : function(oa) {
var parameters= [ var parameters= [
["oauth_timestamp", "1234567"], ["oauth_timestamp", "1234567"],
@ -237,7 +282,7 @@ vows.describe('OAuth').addBatch({
["oauth_version", "1.0"], ["oauth_version", "1.0"],
["oauth_signature_method", "HMAC-SHA1"], ["oauth_signature_method", "HMAC-SHA1"],
["oauth_consumer_key", "asdasdnm2321b3"]]; ["oauth_consumer_key", "asdasdnm2321b3"]];
assert.equal(oa._buildAuthorizationHeaders(parameters), 'OAuth oauth_timestamp="1234567",oauth_nonce="ABCDEF",oauth_version="1.0",oauth_signature_method="HMAC-SHA1",oauth_consumer_key="asdasdnm2321b3"'); assert.equal(oa._buildAuthorizationHeaders(parameters), 'OAuth oauth_timestamp="1234567",oauth_nonce="ABCDEF",oauth_version="1.0",oauth_signature_method="HMAC-SHA1",oauth_consumer_key="asdasdnm2321b3"');
}, },
'*Only* Oauth arguments should be concatentated, others should be disregarded' : function(oa) { '*Only* Oauth arguments should be concatentated, others should be disregarded' : function(oa) {
var parameters= [ var parameters= [
@ -249,7 +294,7 @@ vows.describe('OAuth').addBatch({
["oauth_signature_method", "HMAC-SHA1"], ["oauth_signature_method", "HMAC-SHA1"],
["oauth_consumer_key", "asdasdnm2321b3"], ["oauth_consumer_key", "asdasdnm2321b3"],
["foobar", "asdasdnm2321b3"]]; ["foobar", "asdasdnm2321b3"]];
assert.equal(oa._buildAuthorizationHeaders(parameters), 'OAuth oauth_timestamp="1234567",oauth_nonce="ABCDEF",oauth_version="1.0",oauth_signature_method="HMAC-SHA1",oauth_consumer_key="asdasdnm2321b3"'); assert.equal(oa._buildAuthorizationHeaders(parameters), 'OAuth oauth_timestamp="1234567",oauth_nonce="ABCDEF",oauth_version="1.0",oauth_signature_method="HMAC-SHA1",oauth_consumer_key="asdasdnm2321b3"');
}, },
'_buildAuthorizationHeaders should not depends on Array.prototype.toString' : function(oa) { '_buildAuthorizationHeaders should not depends on Array.prototype.toString' : function(oa) {
var _toString = Array.prototype.toString; var _toString = Array.prototype.toString;
@ -413,7 +458,7 @@ vows.describe('OAuth').addBatch({
var testStringLength= testString.length; var testStringLength= testString.length;
var testStringBytesLength= Buffer.byteLength(testString); var testStringBytesLength= Buffer.byteLength(testString);
assert.notEqual(testStringLength, testStringBytesLength); // Make sure we're testing a string that differs between byte-length and char-length! assert.notEqual(testStringLength, testStringBytesLength); // Make sure we're testing a string that differs between byte-length and char-length!
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
@ -466,7 +511,7 @@ vows.describe('OAuth').addBatch({
"and a post_content_type is specified" : { "and a post_content_type is specified" : {
"It should be written as is, with a content length specified, and the encoding should be set to be as specified" : function(oa) { "It should be written as is, with a content length specified, and the encoding should be set to be as specified" : function(oa) {
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
assert.equal(headers["Content-Type"], "unicorn/encoded"); assert.equal(headers["Content-Type"], "unicorn/encoded");
@ -495,7 +540,7 @@ vows.describe('OAuth').addBatch({
'if no callback is passed' : { 'if no callback is passed' : {
'it should return a request object': function(oa) { 'it should return a request object': function(oa) {
var request= oa.get("http://foo.com/blah", "token", "token_secret") var request= oa.get("http://foo.com/blah", "token", "token_secret")
assert.isObject(request); assert.isObject(request);
assert.equal(request.method, "GET"); assert.equal(request.method, "GET");
request.end(); request.end();
} }
@ -521,7 +566,7 @@ vows.describe('OAuth').addBatch({
oa._createClient= op; oa._createClient= op;
} }
} }
} },
}, },
'PUT' : { 'PUT' : {
'if no callback is passed' : { 'if no callback is passed' : {
@ -656,11 +701,11 @@ vows.describe('OAuth').addBatch({
"and a post_content_type is specified" : { "and a post_content_type is specified" : {
"It should be written as is, with a content length specified, and the encoding should be set to be as specified" : function(oa) { "It should be written as is, with a content length specified, and the encoding should be set to be as specified" : function(oa) {
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
assert.equal(headers["Content-Type"], "unicorn/encoded"); assert.equal(headers["Content-Type"], "unicorn/encoded");
assert.equal(headers["Content-length"], 23); assert.equal(headers["Content-length"], 23);
return { return {
write: function(data) { write: function(data) {
callbackCalled= true; callbackCalled= true;
@ -682,7 +727,7 @@ vows.describe('OAuth').addBatch({
'if no callback is passed' : { 'if no callback is passed' : {
'it should return a request object': function(oa) { 'it should return a request object': function(oa) {
var request= oa.delete("http://foo.com/blah", "token", "token_secret") var request= oa.delete("http://foo.com/blah", "token", "token_secret")
assert.isObject(request); assert.isObject(request);
assert.equal(request.method, "DELETE"); assert.equal(request.method, "DELETE");
request.end(); request.end();
} }
@ -728,7 +773,7 @@ vows.describe('OAuth').addBatch({
} }
finally { finally {
oa._createClient= op; oa._createClient= op;
} }
} }
}, },
'and a 210 response code is received' : { 'and a 210 response code is received' : {
@ -748,7 +793,7 @@ vows.describe('OAuth').addBatch({
} }
finally { finally {
oa._createClient= op; oa._createClient= op;
} }
} }
}, },
'And A 301 redirect is received' : { 'And A 301 redirect is received' : {
@ -817,6 +862,78 @@ vows.describe('OAuth').addBatch({
oa._createClient= op; oa._createClient= op;
} }
} }
},
'and followRedirect is true' : {
'it should (re)perform the secure request but with the new location' : function(oa) {
var op= oa._createClient;
var psr= oa._performSecureRequest;
var responseCounter = 1;
var callbackCalled = false;
var DummyResponse =function() {
if( responseCounter == 1 ){
this.statusCode= 301;
this.headers= {location:"http://redirectto.com"};
responseCounter++;
}
else {
this.statusCode= 200;
}
}
DummyResponse.prototype= events.EventEmitter.prototype;
DummyResponse.prototype.setEncoding= function() {}
try {
oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return new DummyRequest( new DummyResponse() );
}
oa._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) {
if( responseCounter == 1 ) {
assert.equal(url, "http://originalurl.com");
}
else {
assert.equal(url, "http://redirectto.com");
}
return psr.call(oa, oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback )
}
oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function() {
// callback
assert.equal(responseCounter, 2);
callbackCalled= true;
});
assert.equal(callbackCalled, true)
}
finally {
oa._createClient= op;
oa._performSecureRequest= psr;
}
}
},
'and followRedirect is false' : {
'it should not perform the secure request with the new location' : function(oa) {
var op= oa._createClient;
oa.setClientOptions({ followRedirects: false });
var DummyResponse =function() {
this.statusCode= 301;
this.headers= {location:"http://redirectto.com"};
}
DummyResponse.prototype= events.EventEmitter.prototype;
DummyResponse.prototype.setEncoding= function() {}
try {
oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return new DummyRequest( new DummyResponse() );
}
oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function(res, data, response) {
// callback
assert.equal(res.statusCode, 301);
});
}
finally {
oa._createClient= op;
oa.setClientOptions({followRedirects:true});
}
}
} }
}, },
'And A 302 redirect is received' : { 'And A 302 redirect is received' : {
@ -885,7 +1002,79 @@ vows.describe('OAuth').addBatch({
oa._createClient= op; oa._createClient= op;
} }
} }
} },
'and followRedirect is true' : {
'it should (re)perform the secure request but with the new location' : function(oa) {
var op= oa._createClient;
var psr= oa._performSecureRequest;
var responseCounter = 1;
var callbackCalled = false;
var DummyResponse =function() {
if( responseCounter == 1 ){
this.statusCode= 302;
this.headers= {location:"http://redirectto.com"};
responseCounter++;
}
else {
this.statusCode= 200;
}
}
DummyResponse.prototype= events.EventEmitter.prototype;
DummyResponse.prototype.setEncoding= function() {}
try {
oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return new DummyRequest( new DummyResponse() );
}
oa._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) {
if( responseCounter == 1 ) {
assert.equal(url, "http://originalurl.com");
}
else {
assert.equal(url, "http://redirectto.com");
}
return psr.call(oa, oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback )
}
oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function() {
// callback
assert.equal(responseCounter, 2);
callbackCalled= true;
});
assert.equal(callbackCalled, true)
}
finally {
oa._createClient= op;
oa._performSecureRequest= psr;
}
}
},
'and followRedirect is false' : {
'it should not perform the secure request with the new location' : function(oa) {
var op= oa._createClient;
oa.setClientOptions({ followRedirects: false });
var DummyResponse =function() {
this.statusCode= 302;
this.headers= {location:"http://redirectto.com"};
}
DummyResponse.prototype= events.EventEmitter.prototype;
DummyResponse.prototype.setEncoding= function() {}
try {
oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return new DummyRequest( new DummyResponse() );
}
oa._performSecureRequest("token", "token_secret", 'POST', 'http://originalurl.com', {"scope": "foobar,1,2"}, null, null, function(res, data, response) {
// callback
assert.equal(res.statusCode, 302);
});
}
finally {
oa._createClient= op;
oa.setClientOptions({followRedirects:true});
}
}
}
} }
} }
} }

View File

@ -125,12 +125,89 @@ vows.describe('OAuth2').addBatch({
'Given an OAuth2 instance with clientId, clientSecret and customHeaders': { 'Given an OAuth2 instance with clientId, clientSecret and customHeaders': {
topic: new OAuth2("clientId", "clientSecret", undefined, undefined, undefined, topic: new OAuth2("clientId", "clientSecret", undefined, undefined, undefined,
{ 'SomeHeader': '123' }), { 'SomeHeader': '123' }),
'When calling get': { 'When GETing': {
'we should see the custom headers mixed into headers property in options passed to http-library' : function(oa) { 'we should see the custom headers mixed into headers property in options passed to http-library' : function(oa) {
oa._executeRequest= function( http_library, options, callback ) { oa._executeRequest= function( http_library, options, callback ) {
assert.equal(options.headers["SomeHeader"], "123"); assert.equal(options.headers["SomeHeader"], "123");
}; };
oa.get("", {}); oa.get("", {});
},
}
},
'Given an OAuth2 instance with a clientId and clientSecret': {
topic: new OAuth2("clientId", "clientSecret"),
'When POSTing': {
'we should see a given string being sent to the request' : function(oa) {
var bodyWritten= false;
oa._chooseHttpLibrary= function() {
return {
request: function(options) {
assert.equal(options.headers["Content-Type"], "text/plain");
assert.equal(options.headers["Content-Length"], 26);
assert.equal(options.method, "POST");
return {
end: function() {},
on: function() {},
write: function(body) {
bodyWritten= true;
assert.isNotNull(body);
assert.equal(body, "THIS_IS_A_POST_BODY_STRING")
}
}
}
};
}
oa._request("POST", "", {"Content-Type":"text/plain"}, "THIS_IS_A_POST_BODY_STRING");
assert.ok( bodyWritten );
}
},
'When PUTing': {
'we should see a given string being sent to the request' : function(oa) {
var bodyWritten= false;
oa._chooseHttpLibrary= function() {
return {
request: function(options) {
assert.equal(options.headers["Content-Type"], "text/plain");
assert.equal(options.headers["Content-Length"], 25);
assert.equal(options.method, "PUT");
return {
end: function() {},
on: function() {},
write: function(body) {
bodyWritten= true;
assert.isNotNull(body);
assert.equal(body, "THIS_IS_A_PUT_BODY_STRING")
}
}
}
};
}
oa._request("PUT", "", {"Content-Type":"text/plain"}, "THIS_IS_A_PUT_BODY_STRING");
assert.ok( bodyWritten );
}
}
},
'When the user passes in the User-Agent in customHeaders': {
topic: new OAuth2("clientId", "clientSecret", undefined, undefined, undefined,
{ 'User-Agent': '123Agent' }),
'When calling get': {
'we should see the User-Agent mixed into headers property in options passed to http-library' : function(oa) {
oa._executeRequest= function( http_library, options, callback ) {
assert.equal(options.headers["User-Agent"], "123Agent");
};
oa.get("", {});
}
}
},
'When the user does not pass in a User-Agent in customHeaders': {
topic: new OAuth2("clientId", "clientSecret", undefined, undefined, undefined,
undefined),
'When calling get': {
'we should see the default User-Agent mixed into headers property in options passed to http-library' : function(oa) {
oa._executeRequest= function( http_library, options, callback ) {
assert.equal(options.headers["User-Agent"], "Node-oauth");
};
oa.get("", {});
} }
} }
} }