node-oauth/lib/oauth.js

233 lines
7.9 KiB
JavaScript

var sha1= require('./sha1'),
http= require('http');
exports.OAuth= function(requestUrl, accessUrl, authorizeUrl, consumerKey, consumerSecret, version, signatureMethod) {
this._requestUrl= requestUrl;
this._accessUrl= accessUrl;
this._authorizeUrl= authorizeUrl;
this._consumerKey= consumerKey;
this._consumerSecret= this._encodeData( consumerSecret );
this._version= version;
this._signatureMethod= signatureMethod;
};
exports.OAuth.prototype._getTimestamp= function() {
return Math.floor( (new Date()).getTime() / 1000 );
}
exports.OAuth.prototype._encodeData= function(toEncode){
if( toEncode == null || toEncode == "" ) return ""
else {
var result= encodeURIComponent(toEncode);
// Fix the mismatch between OAuth's RFC2396's and Javascript's beliefs in what is right and wrong ;)
return result.replace(/\!/g, "%21")
.replace(/\'/g, "%27")
.replace(/\(/g, "%28")
.replace(/\)/g, "%29")
.replace(/\*/g, "%2A");
}
}
exports.OAuth.prototype._decodeData= function(toDecode) {
if( toDecode != null ) {
toDecode = toDecode.replace(/\+/g, " ");
}
return decodeURIComponent( toDecode);
}
exports.OAuth.prototype._getSignature= function(method, url, parameters, tokenSecret) {
var signatureBase= this._createSignatureBase(method, url, parameters);
return this._createSignature( signatureBase, tokenSecret );
}
exports.OAuth.prototype._splitQueryString= function(stringToSplit) {
var result= {};
var parameters= stringToSplit.split("&");
for(var key in parameters) {
var parameterPair= parameters[key].split("=");
result[parameterPair[0]]= parameterPair[1];
}
return result;
}
// Takes a literal in, then returns a sorted array
exports.OAuth.prototype._sortRequestParams= function(argumentsHash) {
var argument_pairs= [];
for(var key in argumentsHash ) {
argument_pairs[argument_pairs.length]= [key, argumentsHash[key]];
}
// Sort by name, then value.
argument_pairs.sort(function(a,b) {
if ( a[0]== b[0] ) {
return a[1] < b[1] ? -1 : 1;
}
else return a[0] < b[0] ? -1 : 1;
});
return argument_pairs;
}
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+= "="
args+= argument_pairs[i][1];
if( i < argument_pairs.length-1 ) args+= "&";
}
return args;
}
exports.OAuth.prototype._createSignatureBase= function(method, url, parameters) {
url= this._encodeData(url);
parameters= this._encodeData(parameters);
return method.toUpperCase() + "&" + url + "&" + parameters;
}
exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) {
if( tokenSecret === undefined ) var tokenSecret= "";
else tokenSecret= this._encodeData( tokenSecret );
var key= this._consumerSecret + "&" + tokenSecret;
//TODO: whilst we support different signature methods being passed
// we currenting only do SHA1-HMAC
var hash= sha1.HMACSHA1(key, signatureBase);
signature = this._encodeData(hash);
return signature;
}
exports.OAuth.prototype.NONCE_CHARS= ['a','b','c','d','e','f','g','h','i','j','k','l','m','n',
'o','p','q','r','s','t','u','v','w','x','y','z','A','B',
'C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3',
'4','5','6','7','8','9'];
exports.OAuth.prototype._getNonce= function(nonceSize) {
var result = [];
var chars= this.NONCE_CHARS;
var char_pos;
var nonce_chars_length= chars.length;
for (var i = 0; i < nonceSize; i++) {
char_pos= Math.floor(Math.random() * nonce_chars_length);
result[i]= chars[char_pos];
}
return result.join('');
}
exports.OAuth.prototype.getOauthAccessToken= function(oauth_token, oauth_token_secret, callback) {
require('sys').puts('getOauthAccessToken')
var oauthParameters= {
"oauth_timestamp": this._getTimestamp(),
"oauth_nonce": this._getNonce(32),
"oauth_version": this._version,
"oauth_signature_method": this._signatureMethod,
"oauth_consumer_key": this._consumerKey,
"oauth_token": oauth_token
};
var method= "GET";
var sig= this._getSignature( method, this._accessUrl, this._normaliseRequestParams(oauthParameters), oauth_token_secret);
var orderedParameters= this._sortRequestParams( oauthParameters );
orderedParameters[orderedParameters.length]= ["oauth_signature", sig];
var query="";
for( var i= 0 ; i < orderedParameters.length; i++) {
query+= orderedParameters[i][0]+"="+ orderedParameters[i][1] + "&";
}
query= query.substring(0, query.length-1);
var oauthProvider= http.createClient(80, 'twitter.com');
var headers= {'Host': 'twitter.com'}
var request = oauthProvider.request("GET", "/oauth/access_token"+"?"+query, 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= self._splitQueryString(data);
var oauth_token= results["oauth_token"];
results["oauth_token"]= undefined;
var oauth_token_secret= results["oauth_token_secret"];
results["oauth_token_secret"]= undefined;
callback(null, oauth_token, oauth_token_secret, results );
}
});
});
request.end();
}
exports.OAuth.prototype.getOAuthRequestToken= function(callback) {
require('sys').puts('getOauthRequestToken')
var oauthParameters= {
"oauth_timestamp": this._getTimestamp(),
"oauth_nonce": this._getNonce(32),
"oauth_version": this._version,
"oauth_signature_method": this._signatureMethod,
"oauth_consumer_key": this._consumerKey
};
var method= "POST";
require('sys').puts(this._requestUrl)
var sig= this._getSignature( method, this._requestUrl, this._normaliseRequestParams(oauthParameters));
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]+"=\""+orderedParameters[i][1] +"\",";
}
authHeader= authHeader.substring(0, authHeader.length-1);
headers["Authorization"]= authHeader;
headers["Host"] = "twitter.com"
headers["Accept"]= "*/*"
headers["Connection"]= "close"
headers["User-Agent"]= "Express authentication"
headers["Content-length"]= 0
headers["Content-Type"]= "application/x-www-form-urlencoded"
var oauthProvider= http.createClient(80, 'twitter.com');
var request = oauthProvider.request(method, "/oauth/request_token", 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= self._splitQueryString(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, (self._authorizeUrl + oauth_token), results );
}
});
});
request.end();
}