Adds support for 1.0A callback urls, and HTTPS endpoints

Introduces a breaking constructing change, there is now a 'callback url' argument
that can be supplied prior to the signatureMethod in the constructor.

Pass 'null' to explicitly ensure the callback url is never used, 'undefined' to
use 'oob', 'oob' to use 'oob' or an absolute url.
This commit is contained in:
ciaranj 2010-05-09 00:50:06 +01:00
parent 6b7b8f3198
commit 63dc8facb6

View File

@ -1,14 +1,21 @@
var sha1= require('./sha1'), var crypto= require('crypto'),
sha1= require('./sha1'),
http= require('http'), http= require('http'),
URL= require('url'), URL= require('url'),
querystring= require('querystring'); querystring= require('querystring');
exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, version, signatureMethod, nonceSize) { exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, version, authorize_callback, signatureMethod, nonceSize) {
this._requestUrl= requestUrl; this._requestUrl= requestUrl;
this._accessUrl= accessUrl; this._accessUrl= accessUrl;
this._consumerKey= consumerKey; this._consumerKey= consumerKey;
this._consumerSecret= this._encodeData( consumerSecret ); this._consumerSecret= this._encodeData( consumerSecret );
this._version= version; this._version= version;
if( authorize_callback === undefined ) {
this._authorize_callback= "oob";
}
else {
this._authorize_callback= authorize_callback;
}
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1") if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1")
throw new Error("Un-supported signature method: " + signatureMethod ) throw new Error("Un-supported signature method: " + signatureMethod )
@ -81,9 +88,9 @@ exports.OAuth.prototype._normaliseRequestParams= function(arguments) {
var argument_pairs= this._sortRequestParams( arguments ); var argument_pairs= this._sortRequestParams( arguments );
var args= ""; var args= "";
for(var i=0;i<argument_pairs.length;i++) { for(var i=0;i<argument_pairs.length;i++) {
args+= argument_pairs[i][0]; args+= this._encodeData( argument_pairs[i][0] );
args+= "=" args+= "="
args+= argument_pairs[i][1]; args+= this._encodeData( argument_pairs[i][1] );
if( i < argument_pairs.length-1 ) args+= "&"; if( i < argument_pairs.length-1 ) args+= "&";
} }
return args; return args;
@ -91,7 +98,7 @@ exports.OAuth.prototype._normaliseRequestParams= function(arguments) {
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;
} }
@ -119,8 +126,8 @@ exports.OAuth.prototype.NONCE_CHARS= ['a','b','c','d','e','f','g','h','i','j','k
'Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3', 'Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3',
'4','5','6','7','8','9']; '4','5','6','7','8','9'];
exports.OAuth.prototype._createClient= function( port, hostname ) { exports.OAuth.prototype._createClient= function( port, hostname, sshEnabled, credentials ) {
return http.createClient(port, hostname); return http.createClient(port, hostname, sshEnabled, credentials);
} }
exports.OAuth.prototype._getNonce= function(nonceSize) { exports.OAuth.prototype._getNonce= function(nonceSize) {
@ -136,7 +143,7 @@ exports.OAuth.prototype._getNonce= function(nonceSize) {
return result.join(''); return result.join('');
} }
exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, callback ) { exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, callback ) {
var oauthParameters= { var oauthParameters= {
"oauth_timestamp": this._getTimestamp(), "oauth_timestamp": this._getTimestamp(),
"oauth_nonce": this._getNonce(this._nonceSize), "oauth_nonce": this._getNonce(this._nonceSize),
@ -148,6 +155,11 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
if( oauth_token ) { if( oauth_token ) {
oauthParameters["oauth_token"]= oauth_token; oauthParameters["oauth_token"]= oauth_token;
} }
if( extra_params ) {
for( var key in extra_params ) {
oauthParameters[key]= extra_params[key];
}
}
var parsedUrl= URL.parse( url, false ); var parsedUrl= URL.parse( url, false );
if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80; if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80;
@ -166,14 +178,41 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
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+= this._encodeData(orderedParameters[i][0])+"="+ this._encodeData(orderedParameters[i][1]) + "&";
} }
query= query.substring(0, query.length-1); query= query.substring(0, query.length-1);
var oauthProvider= this._createClient(parsedUrl.port, parsedUrl.hostname)
var headers= {'Host': parsedUrl.host}
var request = oauthProvider.request(method, parsedUrl.pathname + "?" + query, headers);
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= {}
// build request authorization header
var authHeader="OAuth ";
for( var i= 0 ; i < orderedParameters.length; i++) {
authHeader+= this._encodeData(orderedParameters[i][0])+"=\""+ this._encodeData(orderedParameters[i][1])+"\",";
}
authHeader= authHeader.substring(0, authHeader.length-1);
headers["Authorization"]= authHeader;
headers["Host"] = parsedUrl.host
headers["Accept"]= "*/*"
headers["Connection"]= "close"
headers["User-Agent"]= "Express authentication"
headers["Content-length"]= 0
headers["Content-Type"]= "application/x-www-form-urlencoded"
var path;
if( parsedUrl.query ) path= parsedUrl.pathname + "?"+ parsedUrl.query ;
else path= parsedUrl.pathname;
var request = oauthProvider.request(method, path , headers);
var data=""; var data="";
var self= this; var self= this;
request.addListener('response', function (response) { request.addListener('response', function (response) {
@ -189,12 +228,12 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
} }
}); });
}); });
request.end(); request.end();
} }
exports.OAuth.prototype.getOauthAccessToken= function(oauth_token, oauth_token_secret, callback) { exports.OAuth.prototype.getOauthAccessToken= function(oauth_token, oauth_token_secret, callback) {
this._performSecureRequest( oauth_token, oauth_token_secret, "GET", this._accessUrl, function(error, data, response) { this._performSecureRequest( oauth_token, oauth_token_secret, "GET", this._accessUrl, null, function(error, data, response) {
if( error ) callback(error); if( error ) callback(error);
else { else {
var results= querystring.parse( data ); var results= querystring.parse( data );
@ -208,68 +247,27 @@ exports.OAuth.prototype.getOauthAccessToken= function(oauth_token, oauth_token_s
} }
exports.OAuth.prototype.getProtectedResource= function(url, method, oauth_token, oauth_token_secret, callback) { exports.OAuth.prototype.getProtectedResource= function(url, method, oauth_token, oauth_token_secret, callback) {
this._performSecureRequest( oauth_token, oauth_token_secret, method, url, callback ); this._performSecureRequest( oauth_token, oauth_token_secret, method, url, null, callback );
} }
exports.OAuth.prototype.getOAuthRequestToken= function(callback) { exports.OAuth.prototype.getOAuthRequestToken= function(callback) {
var oauthParameters= { var extraParams= {};
"oauth_timestamp": this._getTimestamp(), // Callbacks are 1.0A related
"oauth_nonce": this._getNonce(this._nonceSize), if( this._authorize_callback ) {
"oauth_version": this._version, extraParams["oauth_callback"]= this._authorize_callback;
"oauth_signature_method": this._signatureMethod,
"oauth_consumer_key": this._consumerKey
};
var method= "POST";
var sig= this._getSignature( method, this._requestUrl, this._normaliseRequestParams(oauthParameters));
var parsedUrl= URL.parse( this._requestUrl, false );
if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80;
if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443;
var orderedParameters= this._sortRequestParams( oauthParameters );
orderedParameters[orderedParameters.length]= ["oauth_signature", sig];
var headers= {};
// build request authorization header
var authHeader="OAuth ";
for( var i= 0 ; i < orderedParameters.length; i++) {
authHeader+= orderedParameters[i][0]+"=\""+ this._encodeData(orderedParameters[i][1])+"\",";
} }
authHeader= authHeader.substring(0, authHeader.length-1); this._performSecureRequest( null, null, "POST", this._requestUrl, extraParams, function(error, data, response) {
if( error ) callback(error);
else {
var results= querystring.parse(data);
headers["Authorization"]= authHeader; var oauth_token= results["oauth_token"];
headers["Host"] = parsedUrl.host var oauth_token_secret= results["oauth_token_secret"];
headers["Accept"]= "*/*" delete results["oauth_token"];
headers["Connection"]= "close" delete results["oauth_token_secret"];
headers["User-Agent"]= "Express authentication" callback(null, oauth_token, oauth_token_secret, results );
headers["Content-length"]= 0 }
headers["Content-Type"]= "application/x-www-form-urlencoded"
var oauthProvider= this._createClient(parsedUrl.port, parsedUrl.hostname);
var request = oauthProvider.request(method, parsedUrl.pathname, headers);
var data="";
var self= this;
request.addListener('response', function (response) {
response.setEncoding('utf8');
response.addListener('data', function (chunk) {
data+=chunk;
});
response.addListener('end', function () {
if( response.statusCode != 200 ) {
callback( response.statusCode +" : " + data );
} else {
var results= querystring.parse(data);
var oauth_token= results["oauth_token"];
var oauth_token_secret= results["oauth_token_secret"];
delete results["oauth_token"];
delete results["oauth_token_secret"];
callback(null, oauth_token, oauth_token_secret, results );
}
});
}); });
request.end();
} }
exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret, method) { exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret, method) {