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'),
URL= require('url'),
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._accessUrl= accessUrl;
this._consumerKey= consumerKey;
this._consumerSecret= this._encodeData( consumerSecret );
this._version= version;
if( authorize_callback === undefined ) {
this._authorize_callback= "oob";
}
else {
this._authorize_callback= authorize_callback;
}
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1")
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 args= "";
for(var i=0;i<argument_pairs.length;i++) {
args+= argument_pairs[i][0];
args+= this._encodeData( argument_pairs[i][0] );
args+= "="
args+= argument_pairs[i][1];
args+= this._encodeData( argument_pairs[i][1] );
if( i < argument_pairs.length-1 ) args+= "&";
}
return args;
@ -91,7 +98,7 @@ exports.OAuth.prototype._normaliseRequestParams= function(arguments) {
exports.OAuth.prototype._createSignatureBase= function(method, url, parameters) {
url= this._encodeData( this._normalizeUrl(url) );
parameters= this._encodeData(parameters);
parameters= this._encodeData( 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',
'4','5','6','7','8','9'];
exports.OAuth.prototype._createClient= function( port, hostname ) {
return http.createClient(port, hostname);
exports.OAuth.prototype._createClient= function( port, hostname, sshEnabled, credentials ) {
return http.createClient(port, hostname, sshEnabled, credentials);
}
exports.OAuth.prototype._getNonce= function(nonceSize) {
@ -136,7 +143,7 @@ exports.OAuth.prototype._getNonce= function(nonceSize) {
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= {
"oauth_timestamp": this._getTimestamp(),
"oauth_nonce": this._getNonce(this._nonceSize),
@ -148,6 +155,11 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
if( 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 );
if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80;
@ -166,14 +178,41 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
var query="";
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);
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 self= this;
request.addListener('response', function (response) {
@ -189,12 +228,12 @@ exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_toke
}
});
});
request.end();
}
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);
else {
var results= querystring.parse( data );
@ -208,57 +247,18 @@ exports.OAuth.prototype.getOauthAccessToken= function(oauth_token, oauth_token_s
}
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) {
var oauthParameters= {
"oauth_timestamp": this._getTimestamp(),
"oauth_nonce": this._getNonce(this._nonceSize),
"oauth_version": this._version,
"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])+"\",";
var extraParams= {};
// Callbacks are 1.0A related
if( this._authorize_callback ) {
extraParams["oauth_callback"]= this._authorize_callback;
}
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 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 {
this._performSecureRequest( null, null, "POST", this._requestUrl, extraParams, function(error, data, response) {
if( error ) callback(error);
else {
var results= querystring.parse(data);
var oauth_token= results["oauth_token"];
@ -268,8 +268,6 @@ exports.OAuth.prototype.getOAuthRequestToken= function(callback) {
callback(null, oauth_token, oauth_token_secret, results );
}
});
});
request.end();
}
exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret, method) {