Merge branch '0.4_compat_fixes'

Conflicts:
	lib/oauth.js
This commit is contained in:
ciaranj 2011-02-13 11:41:15 +00:00
commit 13d9420c94
5 changed files with 174 additions and 210 deletions

View File

@ -7,8 +7,15 @@ Tested against both Twitter (http://twitter.com), term.ie (http://term.ie/oauth
Also provides rudimentary OAuth2 support, tested against facebook connect and github. For more complete usage examples please take a look Also provides rudimentary OAuth2 support, tested against facebook connect and github. For more complete usage examples please take a look
at connect-auth (http://github.com/ciaranj/connect-auth) at connect-auth (http://github.com/ciaranj/connect-auth)
If you're running a node.js version more recent than 0.4 then you will need to use a version of node.js greater than or equal to 0.9.0.
If you're running a node.js version in the 0.2x stable branc, then you will need to use version 0.8.4.
Please be aware that when moving from 0.8.x to 0.9.0 there are no major API changes your, I've bumped the semi-major version element
so that I can release fixes to the 0.8.x stream if problems come out.
Change History Change History
============== ==============
* 0.9.0 - Compatibility fixes to bring node-oauth up to speed with node.js 0.4x [thanks to Rasmus Andersson for starting the work ]
* 0.8.4 - Fixed issue #14 (Parameter ordering ignored encodings). Added support for repeated parameter names. Implements issue #15 (Use native SHA1 if available, 10x speed improvement!). Fixed issue #16 (Should use POST when requesting access tokens.). Fixed Issue #17 (OAuth2 spec compliance). Implemented enhancement #13 (Adds support for PUT & DELETE http verbs). Fixes issue #18 (Complex/Composite url arguments [thanks novemberborn]) * 0.8.4 - Fixed issue #14 (Parameter ordering ignored encodings). Added support for repeated parameter names. Implements issue #15 (Use native SHA1 if available, 10x speed improvement!). Fixed issue #16 (Should use POST when requesting access tokens.). Fixed Issue #17 (OAuth2 spec compliance). Implemented enhancement #13 (Adds support for PUT & DELETE http verbs). Fixes issue #18 (Complex/Composite url arguments [thanks novemberborn])
* 0.8.3 - Fixed an issue where the auth header code depended on the Array's toString method (Yohei Sasaki) Updated the getOAuthRequestToken method so we can access google's OAuth secured methods. Also re-implemented and fleshed out the test suite. * 0.8.3 - Fixed an issue where the auth header code depended on the Array's toString method (Yohei Sasaki) Updated the getOAuthRequestToken method so we can access google's OAuth secured methods. Also re-implemented and fleshed out the test suite.
* 0.8.2 - The request returning methods will now write the POST body if provided (Chris Anderson), the code responsible for manipulating the headers is a bit safe now when working with other code (Paul McKellar) and tweaked the package.json to use index.js instead of main.js * 0.8.2 - The request returning methods will now write the POST body if provided (Chris Anderson), the code responsible for manipulating the headers is a bit safe now when working with other code (Paul McKellar) and tweaked the package.json to use index.js instead of main.js

View File

@ -11,21 +11,24 @@ var oa= new OAuth("http://term.ie/oauth/example/request_token.php?foo=bar",
"PLAINTEXT") "PLAINTEXT")
oa.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results){ oa.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results){
if(error) sys.puts('error :' + error) if (error) return console.log('error :' + error)
else { console.log('oauth_token :' + oauth_token)
sys.puts('oauth_token :' + oauth_token) console.log('oauth_token_secret :' + oauth_token_secret)
sys.puts('oauth_token_secret :' + oauth_token_secret) console.log('requestoken results :', results)
sys.puts('requestoken results :' + sys.inspect(results)) console.log("Requesting access token")
sys.puts("Requesting access token") oa.getOAuthAccessToken(oauth_token, oauth_token_secret,
oa.getOAuthAccessToken(oauth_token, oauth_token_secret, function(error, oauth_access_token, oauth_access_token_secret, results2) { function(error, oauth_access_token,
sys.puts('oauth_access_token :' + oauth_access_token) oauth_access_token_secret, results2) {
sys.puts('oauth_token_secret :' + oauth_access_token_secret) console.log('oauth_access_token :' + oauth_access_token)
sys.puts('accesstoken results :' + sys.inspect(results2)) console.log('oauth_token_secret :' + oauth_access_token_secret)
sys.puts("Requesting access token") console.log('accesstoken results :', results2)
console.log("Requesting access token")
var data= ""; var data= "";
oa.getProtectedResource("http://term.ie/oauth/example/echo_api.php?foo=bar&too=roo", "GET", oauth_access_token, oauth_access_token_secret, function (error, data, response) { oa.getProtectedResource(
sys.puts(data); "http://term.ie/oauth/example/echo_api.php?foo=bar&too=roo", "GET",
oauth_access_token, oauth_access_token_secret,
function (error, data, response) {
console.log(data);
}); });
}); });
}
}) })

View File

@ -1,6 +1,7 @@
var crypto= require('crypto'), var crypto= require('crypto'),
sha1= require('./sha1'), sha1= require('./sha1'),
http= require('http'), http= require('http'),
https= require('https'),
URL= require('url'), URL= require('url'),
querystring= require('querystring'); querystring= require('querystring');
@ -193,8 +194,21 @@ exports.OAuth.prototype._getNonce= function(nonceSize) {
return result.join(''); return result.join('');
} }
exports.OAuth.prototype._createClient= function( port, hostname, sslEnabled, credentials ) { exports.OAuth.prototype._createClient= function( port, hostname, method, path, headers, sslEnabled ) {
return http.createClient(port, hostname, sslEnabled, credentials); var options = {
host: hostname,
port: port,
path: path,
method: method,
headers: headers
};
var httpModel;
if( sslEnabled ) {
httpModel= https;
} else {
httpModel= http;
}
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 ) {
@ -248,14 +262,6 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80; if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80;
if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443; if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443;
var oauthProvider;
if( parsedUrl.protocol == "https:" ) {
oauthProvider= this._createClient(parsedUrl.port, parsedUrl.hostname, true, crypto.createCredentials({}));
}
else {
oauthProvider= this._createClient(parsedUrl.port, parsedUrl.hostname);
}
var headers= {}; var headers= {};
headers["Authorization"]= this._buildAuthorizationHeaders(orderedParameters); headers["Authorization"]= this._buildAuthorizationHeaders(orderedParameters);
headers["Host"] = parsedUrl.host headers["Host"] = parsedUrl.host
@ -285,16 +291,23 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
if( parsedUrl.query ) path= parsedUrl.pathname + "?"+ parsedUrl.query ; if( parsedUrl.query ) path= parsedUrl.pathname + "?"+ parsedUrl.query ;
else path= parsedUrl.pathname; else path= parsedUrl.pathname;
var request = oauthProvider.request(method, path , headers); var request;
if( parsedUrl.protocol == "https:" ) {
request= this._createClient(parsedUrl.port, parsedUrl.hostname, method, path, headers, true);
}
else {
request= this._createClient(parsedUrl.port, parsedUrl.hostname, method, path, headers);
}
if( callback ) { if( callback ) {
var data=""; var data="";
var self= this; var self= this;
request.addListener('response', function (response) { request.on('response', function (response) {
response.setEncoding('utf8'); response.setEncoding('utf8');
response.addListener('data', function (chunk) { response.on('data', function (chunk) {
data+=chunk; data+=chunk;
}); });
response.addListener('end', function () { response.on('end', function () {
if( response.statusCode != 200 ) { if( response.statusCode != 200 ) {
callback({ statusCode: response.statusCode, data: data }); callback({ statusCode: response.statusCode, data: data });
} else { } else {
@ -303,7 +316,7 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
}); });
}); });
request.socket.addListener("error",callback); request.on("error", callback);
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);

View File

@ -1,10 +1,8 @@
var querystring= require('querystring'), var querystring= require('querystring'),
crypto= require('crypto'), crypto= require('crypto'),
http= require('http'), https= require('https'),
URL= require('url'); URL= require('url');
var sys= require('sys');
exports.OAuth2= function(clientId, clientSecret, baseSite, authorizePath, accessTokenPath) { exports.OAuth2= function(clientId, clientSecret, baseSite, authorizePath, accessTokenPath) {
this._clientId= clientId; this._clientId= clientId;
this._clientSecret= clientSecret; this._clientSecret= clientSecret;
@ -29,7 +27,6 @@ exports.OAuth2.prototype._request= function(method, url, headers, access_token,
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 ) parsedUrl.port= 443; if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443;
var httpClient = http.createClient(parsedUrl.port, parsedUrl.hostname, true, creds);
var realHeaders= {}; var realHeaders= {};
if( headers ) { if( headers ) {
@ -46,17 +43,17 @@ exports.OAuth2.prototype._request= function(method, url, headers, access_token,
parsedUrl.query["access_token"]= access_token; parsedUrl.query["access_token"]= access_token;
} }
var request = httpClient.request(method, parsedUrl.pathname + "?" + querystring.stringify(parsedUrl.query), realHeaders );
httpClient.addListener("secure", function () {
/* // disable verification for now.
var verified = httpClient.verifyPeer();
if(!verified) this.end(); */
});
var result= ""; var result= "";
request.addListener('response', function (response) {
var options = {
host:parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.pathname + "?" + querystring.stringify(parsedUrl.query),
method: method,
headers: realHeaders
};
request = https.request(options, function (response) {
response.addListener("data", function (chunk) { response.addListener("data", function (chunk) {
result+= chunk result+= chunk
}); });
@ -69,6 +66,10 @@ var verified = httpClient.verifyPeer();
}); });
}); });
request.on('error', function(e) {
callback(e);
});
request.end(); request.end();
} }

View File

@ -128,21 +128,19 @@ vows.describe('OAuth').addBatch({
var oa= new OAuth(null, null, null, null, null, null, "HMAC-SHA1"), var oa= new OAuth(null, null, null, null, null, null, "HMAC-SHA1"),
mockProvider= {}; mockProvider= {};
mockProvider.request= function(method, path, headers) { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
assert.equal(headers.Host, "somehost.com:8080"); assert.equal(headers.Host, "somehost.com:8080");
return result= {addListener:function(){}, assert.equal(hostname, "somehost.com");
end:function(){}, assert.equal(port, "8080");
socket: {addListener: function(){}}}; return {
} on: function() {},
oa._createClient= function(port, host) { end: function() {}
assert.equal(port, '8080'); };
assert.equal(host, 'somehost.com');
return mockProvider;
} }
return oa; return oa;
}, },
'getProtectedResrouce should correctly define the host headers': function(oa) { 'getProtectedResource should correctly define the host headers': function(oa) {
oa.getProtectedResource("http://somehost.com:8080", "GET", "oauth_token", null, function(){require('sys').p('dddd')}) oa.getProtectedResource("http://somehost.com:8080", "GET", "oauth_token", null, function(){})
} }
}, },
'When building the OAuth Authorization header': { 'When building the OAuth Authorization header': {
@ -194,20 +192,13 @@ vows.describe('OAuth').addBatch({
var post_body_written= false; var post_body_written= false;
var op= oa._createClient; var op= oa._createClient;
try { try {
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return {
request: function(method, path, headers) {
return { return {
write: function(post_body){ write: function(post_body){
post_body_written= true; post_body_written= true;
assert.equal(post_body,"scope=foobar%2C1%2C2"); assert.equal(post_body,"scope=foobar%2C1%2C2");
},
socket: {addListener: function(){}},
addListener: function() {},
end: function() {}
}
}
} }
};
} }
oa._performSecureRequest("token", "token_secret", 'POST', 'http://foo.com/protected_resource', {"scope": "foobar,1,2"}); oa._performSecureRequest("token", "token_secret", 'POST', 'http://foo.com/protected_resource', {"scope": "foobar,1,2"});
assert.equal(post_body_written, true); assert.equal(post_body_written, true);
@ -237,19 +228,14 @@ vows.describe('OAuth').addBatch({
var callbackCalled= false; var callbackCalled= false;
var op= oa._createClient; var op= oa._createClient;
try { try {
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return {
request: function(method, path, headers) {
return { return {
write: function(){}, write: function(){},
socket: {addListener: function(){}}, on: function() {},
addListener: function() {},
end: function() { end: function() {
callbackCalled= true; callbackCalled= true;
} }
} };
}
}
} }
var request= oa.post("http://foo.com/blah", "token", "token_secret", "BLAH", "text/plain", function(e,d){}) var request= oa.post("http://foo.com/blah", "token", "token_secret", "BLAH", "text/plain", function(e,d){})
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);
@ -265,21 +251,17 @@ vows.describe('OAuth').addBatch({
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return {
request: function(method, path, headers) {
assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded") assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded")
return { return {
socket: {addListener: function(){}},
write: function(data){ write: function(data){
callbackCalled= true; callbackCalled= true;
assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2");
}, },
addListener: function() {}, on: function() {},
end: function() {} end: function() {
}
}
} }
};
} }
var request= oa.post("http://foo.com/blah", "token", "token_secret", {"foo":"1,2,3", "bar":"1+2"}) var request= oa.post("http://foo.com/blah", "token", "token_secret", {"foo":"1,2,3", "bar":"1+2"})
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);
@ -295,22 +277,18 @@ vows.describe('OAuth').addBatch({
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return {
request: function(method, path, headers) {
assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded"); assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded");
assert.equal(headers["Content-length"], 23); assert.equal(headers["Content-length"], 23);
return { return {
socket: {addListener: function(){}},
write: function(data){ write: function(data){
callbackCalled= true; callbackCalled= true;
assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2");
}, },
addListener: function() {}, on: function() {},
end: function() {} end: function() {
}
}
} }
};
} }
var request= oa.post("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2") var request= oa.post("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2")
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);
@ -325,22 +303,18 @@ vows.describe('OAuth').addBatch({
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return {
request: function(method, path, headers) {
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 {
socket: {addListener: function(){}},
write: function(data){ write: function(data){
callbackCalled= true; callbackCalled= true;
assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2");
}, },
addListener: function() {}, on: function() {},
end: function() {} end: function() {
}
}
} }
};
} }
var request= oa.post("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2", "unicorn/encoded") var request= oa.post("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2", "unicorn/encoded")
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);
@ -366,18 +340,13 @@ vows.describe('OAuth').addBatch({
var callbackCalled= false; var callbackCalled= false;
var op= oa._createClient; var op= oa._createClient;
try { try {
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return { return {
request: function(method, path, headers) { on: function() {},
return {
socket: {addListener: function(){}},
addListener: function() {},
end: function() { end: function() {
callbackCalled= true; callbackCalled= true;
} }
} };
}
}
} }
var request= oa.get("http://foo.com/blah", "token", "token_secret", function(e,d) {}) var request= oa.get("http://foo.com/blah", "token", "token_secret", function(e,d) {})
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);
@ -400,25 +369,22 @@ vows.describe('OAuth').addBatch({
}, },
'if a callback is passed' : { 'if a callback is passed' : {
"it should call the internal request's end method and return nothing": function(oa) { "it should call the internal request's end method and return nothing": function(oa) {
var callbackCalled= false; var callbackCalled= 0;
var op= oa._createClient; var op= oa._createClient;
try { try {
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return { return {
request: function(method, path, headers) { on: function() {},
return { write: function(data) {
write: function(){}, callbackCalled++;
socket: {addListener: function(){}}, },
addListener: function() {},
end: function() { end: function() {
callbackCalled= true; callbackCalled++;
}
}
}
} }
};
} }
var request= oa.put("http://foo.com/blah", "token", "token_secret", "BLAH", "text/plain", function(e,d){}) var request= oa.put("http://foo.com/blah", "token", "token_secret", "BLAH", "text/plain", function(e,d){})
assert.equal(callbackCalled, true); assert.equal(callbackCalled, 2);
assert.isUndefined(request); assert.isUndefined(request);
} }
finally { finally {
@ -431,21 +397,14 @@ vows.describe('OAuth').addBatch({
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return {
request: function(method, path, headers) {
assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded") assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded")
return { return {
socket: {addListener: function(){}},
write: function(data) { write: function(data) {
callbackCalled= true; callbackCalled= true;
assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2");
},
addListener: function() {},
end: function() {}
}
}
} }
};
} }
var request= oa.put("http://foo.com/blah", "token", "token_secret", {"foo":"1,2,3", "bar":"1+2"}) var request= oa.put("http://foo.com/blah", "token", "token_secret", {"foo":"1,2,3", "bar":"1+2"})
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);
@ -461,22 +420,15 @@ vows.describe('OAuth').addBatch({
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return {
request: function(method, path, headers) {
assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded"); assert.equal(headers["Content-Type"], "application/x-www-form-urlencoded");
assert.equal(headers["Content-length"], 23); assert.equal(headers["Content-length"], 23);
return { return {
socket: {addListener: function(){}},
write: function(data) { write: function(data) {
callbackCalled= true; callbackCalled= true;
assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2");
},
addListener: function() {},
end: function() {}
}
}
} }
};
} }
var request= oa.put("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2") var request= oa.put("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2")
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);
@ -491,22 +443,15 @@ vows.describe('OAuth').addBatch({
var op= oa._createClient; var op= oa._createClient;
try { try {
var callbackCalled= false; var callbackCalled= false;
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return {
request: function(method, path, headers) {
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 {
socket: {addListener: function(){}},
write: function(data) { write: function(data) {
callbackCalled= true; callbackCalled= true;
assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2"); assert.equal(data, "foo=1%2C2%2C3&bar=1%2B2");
},
addListener: function() {},
end: function() {}
}
}
} }
};
} }
var request= oa.put("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2", "unicorn/encoded") var request= oa.put("http://foo.com/blah", "token", "token_secret", "foo=1%2C2%2C3&bar=1%2B2", "unicorn/encoded")
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);
@ -532,18 +477,13 @@ vows.describe('OAuth').addBatch({
var callbackCalled= false; var callbackCalled= false;
var op= oa._createClient; var op= oa._createClient;
try { try {
oa._createClient= function() { oa._createClient= function( port, hostname, method, path, headers, sshEnabled ) {
return { return {
request: function(method, path, headers) { on: function() {},
return {
socket: {addListener: function(){}},
addListener: function() {},
end: function() { end: function() {
callbackCalled= true; callbackCalled= true;
} }
} };
}
}
} }
var request= oa.delete("http://foo.com/blah", "token", "token_secret", function(e,d) {}) var request= oa.delete("http://foo.com/blah", "token", "token_secret", function(e,d) {})
assert.equal(callbackCalled, true); assert.equal(callbackCalled, true);