Skip to content

Commit 4e351fe

Browse files
committed
Merge remote-tracking branch 'origin/master' into evanp-issue144
2 parents ff2210a + 00ae962 commit 4e351fe

File tree

6 files changed

+384
-82
lines changed

6 files changed

+384
-82
lines changed

Readme.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Tested against Twitter (http://twitter.com), term.ie (http://term.ie/oauth/examp
66

77
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)
88

9+
[![Clone in Koding](http://learn.koding.com/btn/clone_d.png)][koding]
10+
[koding]: https://koding.com/Teamwork?import=https://github.com/ciaranj/node-oauth/archive/master.zip&c=git1
911

1012
Installation
1113
==============
@@ -22,7 +24,7 @@ To run examples/tests insall Mocha `$ npm install -g mocha` and run `$ mocha you
2224

2325
```javascript
2426
describe('OAuth1.0',function(){
25-
var OAuth = require('OAuth');
27+
var OAuth = require('oauth');
2628

2729
it('tests trends Twitter API v1.1',function(done){
2830
var oauth = new OAuth.OAuth(
@@ -36,7 +38,7 @@ describe('OAuth1.0',function(){
3638
);
3739
oauth.get(
3840
'https://api.twitter.com/1.1/trends/place.json?id=23424977',
39-
'your user toke for this app', //test user token
41+
'your user token for this app', //test user token
4042
'your user secret for this app', //test user secret
4143
function (e, data, res){
4244
if (e) console.error(e);
@@ -50,7 +52,7 @@ describe('OAuth1.0',function(){
5052
## OAuth2.0
5153
```javascript
5254
describe('OAuth2',function(){
53-
var OAuth = require('OAuth');
55+
var OAuth = require('oauth');
5456

5557
it('gets bearer token', function(done){
5658
var OAuth2 = OAuth.OAuth2;
@@ -75,6 +77,11 @@ describe('OAuth2',function(){
7577
Change History
7678
==============
7779
80+
* 0.9.11
81+
- OAuth2: No longer sends the type=webserver argument with the OAuth2 requests (thank you bendiy)
82+
- OAuth2: Provides a default (and overrideable) User-Agent header (thanks to Andrew Martens & Daniel Mahlow)
83+
- OAuth1: New followRedirects client option (true by default) (thanks to Pieter Joost van de Sande)
84+
- OAuth1: Adds RSA-SHA1 support (thanks to Jeffrey D. Van Alstine & Michael Garvin & Andreas Knecht)
7885
* 0.9.10
7986
- OAuth2: Addresses 2 issues that came in with 0.9.9, #129 & #125 (thank you José F. Romaniello)
8087
* 0.9.9
@@ -156,3 +163,10 @@ Contributors (In no particular order)
156163
* rolandboon - http://rolandboon.com
157164
* Brian Park - http://github.com/yaru22
158165
* José F. Romaniello - http://github.com/jfromaniello
166+
* bendiy - https://github.com/bendiy
167+
* Andrew Martins - http://www.andrewmartens.com
168+
* Daniel Mahlow - https://github.com/dmahlow
169+
* Pieter Joost van de Sande - https://github.com/pjvds
170+
* Jeffrey D. Van Alstine
171+
* Michael Garvin
172+
* Andreas Knecht

lib/oauth.js

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, vers
1313
this._accessUrl= accessUrl;
1414
this._consumerKey= consumerKey;
1515
this._consumerSecret= this._encodeData( consumerSecret );
16+
if (signatureMethod == "RSA-SHA1") {
17+
this._privateKey = consumerSecret;
18+
}
1619
this._version= version;
1720
if( authorize_callback === undefined ) {
1821
this._authorize_callback= "oob";
@@ -21,15 +24,16 @@ exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, vers
2124
this._authorize_callback= authorize_callback;
2225
}
2326

24-
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1")
27+
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-SHA1")
2528
throw new Error("Un-supported signature method: " + signatureMethod )
2629
this._signatureMethod= signatureMethod;
2730
this._nonceSize= nonceSize || 32;
2831
this._headers= customHeaders || {"Accept" : "*/*",
2932
"Connection" : "close",
3033
"User-Agent" : "Node authentication"}
3134
this._clientOptions= this._defaultClientOptions= {"requestTokenHttpMethod": "POST",
32-
"accessTokenHttpMethod": "POST"};
35+
"accessTokenHttpMethod": "POST",
36+
"followRedirects": true};
3337
this._oauthParameterSeperator = ",";
3438
};
3539

@@ -40,9 +44,12 @@ exports.OAuthEcho= function(realm, verify_credentials, consumerKey, consumerSecr
4044
this._verifyCredentials = verify_credentials;
4145
this._consumerKey= consumerKey;
4246
this._consumerSecret= this._encodeData( consumerSecret );
47+
if (signatureMethod == "RSA-SHA1") {
48+
this._privateKey = consumerSecret;
49+
}
4350
this._version= version;
4451

45-
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1")
52+
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-SHA1")
4653
throw new Error("Un-supported signature method: " + signatureMethod );
4754
this._signatureMethod= signatureMethod;
4855
this._nonceSize= nonceSize || 32;
@@ -86,15 +93,15 @@ exports.OAuth.prototype._getSignature= function(method, url, parameters, tokenSe
8693
exports.OAuth.prototype._normalizeUrl= function(url) {
8794
var parsedUrl= URL.parse(url, true)
8895
var port ="";
89-
if( parsedUrl.port ) {
96+
if( parsedUrl.port ) {
9097
if( (parsedUrl.protocol == "http:" && parsedUrl.port != "80" ) ||
9198
(parsedUrl.protocol == "https:" && parsedUrl.port != "443") ) {
9299
port= ":" + parsedUrl.port;
93100
}
94101
}
95102

96103
if( !parsedUrl.pathname || parsedUrl.pathname == "" ) parsedUrl.pathname ="/";
97-
104+
98105
return parsedUrl.protocol + "//" + parsedUrl.hostname + port + parsedUrl.pathname;
99106
}
100107

@@ -124,7 +131,7 @@ exports.OAuth.prototype._buildAuthorizationHeaders= function(orderedParameters)
124131
}
125132
}
126133

127-
authHeader= authHeader.substring(0, authHeader.length-this._oauthParameterSeperator.length);
134+
authHeader= authHeader.substring(0, authHeader.length-this._oauthParameterSeperator.length);
128135
return authHeader;
129136
}
130137

@@ -143,66 +150,70 @@ exports.OAuth.prototype._makeArrayOfArgumentsHash= function(argumentsHash) {
143150
argument_pairs[argument_pairs.length]= [key, value];
144151
}
145152
}
146-
return argument_pairs;
147-
}
153+
return argument_pairs;
154+
}
148155

149156
// Sorts the encoded key value pairs by encoded name, then encoded value
150157
exports.OAuth.prototype._sortRequestParams= function(argument_pairs) {
151158
// Sort by name, then value.
152159
argument_pairs.sort(function(a,b) {
153160
if ( a[0]== b[0] ) {
154-
return a[1] < b[1] ? -1 : 1;
161+
return a[1] < b[1] ? -1 : 1;
155162
}
156-
else return a[0] < b[0] ? -1 : 1;
163+
else return a[0] < b[0] ? -1 : 1;
157164
});
158165

159166
return argument_pairs;
160167
}
161168

162-
exports.OAuth.prototype._normaliseRequestParams= function(arguments) {
163-
var argument_pairs= this._makeArrayOfArgumentsHash(arguments);
169+
exports.OAuth.prototype._normaliseRequestParams= function(args) {
170+
var argument_pairs= this._makeArrayOfArgumentsHash(args);
164171
// First encode them #3.4.1.3.2 .1
165172
for(var i=0;i<argument_pairs.length;i++) {
166173
argument_pairs[i][0]= this._encodeData( argument_pairs[i][0] );
167174
argument_pairs[i][1]= this._encodeData( argument_pairs[i][1] );
168175
}
169-
176+
170177
// Then sort them #3.4.1.3.2 .2
171178
argument_pairs= this._sortRequestParams( argument_pairs );
172-
179+
173180
// Then concatenate together #3.4.1.3.2 .3 & .4
174181
var args= "";
175182
for(var i=0;i<argument_pairs.length;i++) {
176183
args+= argument_pairs[i][0];
177184
args+= "="
178185
args+= argument_pairs[i][1];
179186
if( i < argument_pairs.length-1 ) args+= "&";
180-
}
187+
}
181188
return args;
182189
}
183190

184191
exports.OAuth.prototype._createSignatureBase= function(method, url, parameters) {
185-
url= this._encodeData( this._normalizeUrl(url) );
192+
url= this._encodeData( this._normalizeUrl(url) );
186193
parameters= this._encodeData( parameters );
187194
return method.toUpperCase() + "&" + url + "&" + parameters;
188195
}
189196

190197
exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) {
191198
if( tokenSecret === undefined ) var tokenSecret= "";
192-
else tokenSecret= this._encodeData( tokenSecret );
199+
else tokenSecret= this._encodeData( tokenSecret );
193200
// consumerSecret is already encoded
194201
var key= this._consumerSecret + "&" + tokenSecret;
195202

196203
var hash= ""
197204
if( this._signatureMethod == "PLAINTEXT" ) {
198205
hash= key;
199206
}
207+
else if (this._signatureMethod == "RSA-SHA1") {
208+
key = this._privateKey || "";
209+
hash= crypto.createSign("RSA-SHA1").update(signatureBase).sign(key, 'base64');
210+
}
200211
else {
201212
if( crypto.Hmac ) {
202213
hash = crypto.createHmac("sha1", key).update(signatureBase).digest("base64");
203214
}
204215
else {
205-
hash= sha1.HMACSHA1(key, signatureBase);
216+
hash= sha1.HMACSHA1(key, signatureBase);
206217
}
207218
}
208219
return hash;
@@ -218,7 +229,7 @@ exports.OAuth.prototype._getNonce= function(nonceSize) {
218229
var chars= this.NONCE_CHARS;
219230
var char_pos;
220231
var nonce_chars_length= chars.length;
221-
232+
222233
for (var i = 0; i < nonceSize; i++) {
223234
char_pos= Math.floor(Math.random() * nonce_chars_length);
224235
result[i]= chars[char_pos];
@@ -240,7 +251,7 @@ exports.OAuth.prototype._createClient= function( port, hostname, method, path, h
240251
} else {
241252
httpModel= http;
242253
}
243-
return httpModel.request(options);
254+
return httpModel.request(options);
244255
}
245256

246257
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
361372
request= this._createClient(parsedUrl.port, parsedUrl.hostname, method, path, headers);
362373
}
363374

375+
var clientOptions = this._clientOptions;
364376
if( callback ) {
365-
var data="";
377+
var data="";
366378
var self= this;
367379

368380
// Some hosts *cough* google appear to close the connection early / send no content-length header
369381
// allow this behaviour.
370382
var allowEarlyClose= OAuthUtils.isAnEarlyCloseHost( parsedUrl.hostname );
371383
var callbackCalled= false;
372-
function passBackControl( response ) {
384+
var passBackControl = function( response ) {
373385
if(!callbackCalled) {
374386
callbackCalled= true;
375387
if ( response.statusCode >= 200 && response.statusCode <= 299 ) {
376388
callback(null, data, response);
377389
} else {
378390
// Follow 301 or 302 redirects with Location HTTP header
379-
if((response.statusCode == 301 || response.statusCode == 302) && response.headers && response.headers.location) {
391+
if((response.statusCode == 301 || response.statusCode == 302) && clientOptions.followRedirects && response.headers && response.headers.location) {
380392
self._performSecureRequest( oauth_token, oauth_token_secret, method, response.headers.location, extra_params, post_body, post_content_type, callback);
381393
}
382394
else {
@@ -400,12 +412,14 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
400412
}
401413
});
402414
});
403-
415+
404416
request.on("error", function(err) {
405-
callbackCalled= true;
406-
callback( err )
417+
if(!callbackCalled) {
418+
callbackCalled= true;
419+
callback( err )
420+
}
407421
});
408-
422+
409423
if( (method == "POST" || method =="PUT") && post_body != null && post_body != "" ) {
410424
request.write(post_body);
411425
}
@@ -417,7 +431,7 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
417431
}
418432
return request;
419433
}
420-
434+
421435
return;
422436
}
423437

@@ -444,7 +458,7 @@ exports.OAuth.prototype.getOAuthAccessToken= function(oauth_token, oauth_token_s
444458
} else {
445459
extraParams.oauth_verifier= oauth_verifier;
446460
}
447-
461+
448462
this._performSecureRequest( oauth_token, oauth_token_secret, this._clientOptions.accessTokenHttpMethod, this._accessUrl, extraParams, null, null, function(error, data, response) {
449463
if( error ) callback(error);
450464
else {
@@ -484,7 +498,7 @@ exports.OAuth.prototype._putOrPost= function(method, url, oauth_token, oauth_tok
484498
}
485499
return this._performSecureRequest( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback );
486500
}
487-
501+
488502

489503
exports.OAuth.prototype.put= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) {
490504
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
500514
*
501515
* The callback should expect a function of the following form:
502516
*
503-
* function(err, token, token_secret, parsedQueryString) {}
517+
* function(err, token, token_secret, parsedQueryString) {}
504518
*
505519
* This method has optional parameters so can be called in the following 2 ways:
506520
*
@@ -519,7 +533,7 @@ exports.OAuth.prototype.getOAuthRequestToken= function( extraParams, callback )
519533
callback = extraParams;
520534
extraParams = {};
521535
}
522-
// Callbacks are 1.0A related
536+
// Callbacks are 1.0A related
523537
if( this._authorize_callback ) {
524538
extraParams["oauth_callback"]= this._authorize_callback;
525539
}
@@ -546,12 +560,12 @@ exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret,
546560
var orderedParameters= this._prepareParameters(oauth_token, oauth_token_secret, method, url, {});
547561
var parsedUrl= URL.parse( url, false );
548562

549-
var query="";
563+
var query="";
550564
for( var i= 0 ; i < orderedParameters.length; i++) {
551565
query+= orderedParameters[i][0]+"="+ this._encodeData(orderedParameters[i][1]) + "&";
552566
}
553567
query= query.substring(0, query.length-1);
554-
568+
555569
return parsedUrl.protocol + "//"+ parsedUrl.host + parsedUrl.pathname + "?" + query;
556570
};
557571

0 commit comments

Comments
 (0)