forked from ciaranj/node-oauth
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9fea0b5
commit 0158a78
Showing
2 changed files
with
324 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
exports.OAuth = require("./lib/oauth").OAuth; | ||
exports.OAuth = require("./lib/oauth").OAuth; | ||
exports.OAuthEcho = require("./lib/oauthecho").OAuthEcho; | ||
exports.OAuth2 = require("./lib/oauth2").OAuth2; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,322 @@ | ||
var crypto= require('crypto'), | ||
sha1= require('./sha1'), | ||
http= require('http'), | ||
https= require('https'), | ||
URL= require('url'), | ||
querystring= require('querystring'); | ||
|
||
exports.OAuthEcho= function(realm, verify_credentials, consumerKey, consumerSecret, version, signatureMethod, nonceSize, customHeaders) { | ||
this._realm= realm; | ||
this._verifyCredentials = verify_credentials; | ||
this._consumerKey= consumerKey; | ||
this._consumerSecret= this._encodeData( consumerSecret ); | ||
this._version= version; | ||
|
||
if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1") | ||
throw new Error("Un-supported signature method: " + signatureMethod ); | ||
this._signatureMethod= signatureMethod; | ||
this._nonceSize= nonceSize || 32; | ||
this._headers= customHeaders || {"Accept" : "*/*", | ||
"Connection" : "close", | ||
"User-Agent" : "Node authentication"}; | ||
} | ||
|
||
exports.OAuthEcho.prototype.post = function(url, oauth_token, oauth_secret, post_body, post_content_type, callback) { | ||
|
||
var extra_params= null; | ||
if( typeof post_content_type == "function" ) { | ||
callback= post_content_type; | ||
post_content_type= null; | ||
} | ||
if( typeof post_body != "string" ) { | ||
post_content_type= "application/x-www-form-urlencoded" | ||
extra_params= post_body; | ||
post_body= null; | ||
} | ||
return this._performSecureRequest( oauth_token, oauth_secret, "POST", url, extra_params, post_body, post_content_type, callback ); | ||
} | ||
|
||
exports.OAuthEcho.prototype._encodeData= function(toEncode){ | ||
if( toEncode == null || toEncode == "" ) return "" | ||
else { | ||
var result= encodeURIComponent(toEncode); | ||
// Fix the mismatch between OAuth's RFC3986'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.OAuthEcho.prototype._getSignature= function(method, url, parameters, tokenSecret) { | ||
var signatureBase= this._createSignatureBase(method, url, parameters); | ||
return this._createSignature( signatureBase, tokenSecret ); | ||
} | ||
|
||
exports.OAuthEcho.prototype._normalizeUrl= function(url) { | ||
var parsedUrl= URL.parse(url, true) | ||
var port =""; | ||
if( parsedUrl.port ) { | ||
if( (parsedUrl.protocol == "http:" && parsedUrl.port != "80" ) || | ||
(parsedUrl.protocol == "https:" && parsedUrl.port != "443") ) { | ||
port= ":" + parsedUrl.port; | ||
} | ||
} | ||
|
||
if( !parsedUrl.pathname || parsedUrl.pathname == "" ) parsedUrl.pathname ="/"; | ||
|
||
return parsedUrl.protocol + "//" + parsedUrl.hostname + port + parsedUrl.pathname; | ||
} | ||
|
||
exports.OAuthEcho.prototype._getTimestamp= function() { | ||
return Math.floor( (new Date()).getTime() / 1000 ); | ||
} | ||
|
||
// Is the parameter considered an OAuth parameter | ||
exports.OAuthEcho.prototype._isParameterNameAnOAuthParameter= function(parameter) { | ||
var m = parameter.match('^oauth_'); | ||
if( m && ( m[0] === "oauth_" ) ) { | ||
return true; | ||
} | ||
else { | ||
return false; | ||
} | ||
}; | ||
|
||
exports.OAuthEcho.prototype._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ) { | ||
|
||
// Here we use verify_credentials instead of url | ||
var orderedParameters= this._prepareParameters(url, oauth_token, oauth_token_secret, method, extra_params); | ||
|
||
if( !post_content_type ) { | ||
post_content_type= "application/x-www-form-urlencoded"; | ||
} | ||
var parsedUrl= URL.parse( url, false ); | ||
if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80; | ||
if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443; | ||
|
||
var headers= {}; | ||
|
||
// OAuth Echo header | ||
headers["X-Verify-Credentials-Authorization"]= this._buildAuthorizationHeaders(orderedParameters); | ||
headers["Host"] = parsedUrl.host | ||
|
||
for( var key in this._headers ) { | ||
if (this._headers.hasOwnProperty(key)) { | ||
headers[key]= this._headers[key]; | ||
} | ||
} | ||
|
||
// Filter out any passed extra_params that are really to do with OAuth | ||
for(var key in extra_params) { | ||
if( this._isParameterNameAnOAuthParameter( key ) ) { | ||
delete extra_params[key]; | ||
} | ||
} | ||
|
||
if( (method == "POST" || method == "PUT") && ( post_body == null && extra_params != null) ) { | ||
post_body= querystring.stringify(extra_params); | ||
} | ||
|
||
headers["Content-length"]= post_body ? post_body.length : 0; //Probably going to fail if not posting ascii | ||
headers["Content-Type"]= post_content_type; | ||
|
||
var path; | ||
if( !parsedUrl.pathname || parsedUrl.pathname == "" ) parsedUrl.pathname ="/"; | ||
if( parsedUrl.query ) path= parsedUrl.pathname + "?"+ parsedUrl.query ; | ||
else path= parsedUrl.pathname; | ||
|
||
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 ) { | ||
var data=""; | ||
var self= this; | ||
request.on('response', function (response) { | ||
response.setEncoding('utf8'); | ||
response.on('data', function (chunk) { | ||
data+=chunk; | ||
}); | ||
response.on('end', function () { | ||
if( response.statusCode != 200 ) { | ||
// Follow 302 redirects with Location HTTP header | ||
if(response.statusCode == 302 && response.headers && response.headers.location) { | ||
self._performSecureRequest( oauth_token, oauth_token_secret, method, response.headers.location, extra_params, post_body, post_content_type, callback); | ||
} | ||
else { | ||
callback({ statusCode: response.statusCode, data: data }, data, response); | ||
} | ||
} else { | ||
callback(null, data, response); | ||
} | ||
}); | ||
}); | ||
|
||
request.on("error", callback); | ||
|
||
if( (method == "POST" || method =="PUT") && post_body != null && post_body != "" ) { | ||
request.write(post_body); | ||
} | ||
request.end(); | ||
} | ||
else { | ||
if( (method == "POST" || method =="PUT") && post_body != null && post_body != "" ) { | ||
request.write(post_body); | ||
} | ||
return request; | ||
} | ||
|
||
return; | ||
} | ||
|
||
exports.OAuthEcho.prototype._createClient= function( port, hostname, method, path, headers, sslEnabled ) { | ||
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.OAuthEcho.prototype._prepareParameters= function( url, oauth_token, oauth_token_secret, method, extra_params ) { | ||
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, | ||
"oauth_token": oauth_token | ||
}; | ||
|
||
var sig= this._getSignature( "GET", this._verifyCredentials, this._normaliseRequestParams(oauthParameters), oauth_token_secret); | ||
var orderedParameters= this._sortRequestParams( this._makeArrayOfArgumentsHash(oauthParameters) ); | ||
orderedParameters[orderedParameters.length]= ["oauth_signature", sig]; | ||
|
||
return orderedParameters; | ||
} | ||
|
||
// build the OAuth request authorization header | ||
exports.OAuthEcho.prototype._buildAuthorizationHeaders= function(orderedParameters) { | ||
var authHeader='OAuth realm="' + this._realm + '"'; | ||
for( var i= 0 ; i < orderedParameters.length; i++) { | ||
// Whilst the all the parameters should be included within the signature, only the oauth_ arguments | ||
// should appear within the authorization header. | ||
if( this._isParameterNameAnOAuthParameter(orderedParameters[i][0]) ) { | ||
authHeader+= ", " + this._encodeData(orderedParameters[i][0])+"=\""+ this._encodeData(orderedParameters[i][1])+"\""; | ||
} | ||
} | ||
authHeader= authHeader.substring(0, authHeader.length-1); | ||
return authHeader; | ||
} | ||
|
||
// Takes an object literal that represents the arguments, and returns an array | ||
// of argument/value pairs. | ||
exports.OAuthEcho.prototype._makeArrayOfArgumentsHash= function(argumentsHash) { | ||
var argument_pairs= []; | ||
for(var key in argumentsHash ) { | ||
var value= argumentsHash[key]; | ||
if( Array.isArray(value) ) { | ||
for(var i=0;i<value.length;i++) { | ||
argument_pairs[argument_pairs.length]= [key, value[i]]; | ||
} | ||
} | ||
else { | ||
argument_pairs[argument_pairs.length]= [key, value]; | ||
} | ||
} | ||
return argument_pairs; | ||
} | ||
|
||
// Sorts the encoded key value pairs by encoded name, then encoded value | ||
exports.OAuthEcho.prototype._sortRequestParams= function(argument_pairs) { | ||
// 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.OAuthEcho.prototype._normaliseRequestParams= function(arguments) { | ||
var argument_pairs= this._makeArrayOfArgumentsHash(arguments); | ||
// First encode them #3.4.1.3.2 .1 | ||
for(var i=0;i<argument_pairs.length;i++) { | ||
argument_pairs[i][0]= this._encodeData( argument_pairs[i][0] ); | ||
argument_pairs[i][1]= this._encodeData( argument_pairs[i][1] ); | ||
} | ||
|
||
// Then sort them #3.4.1.3.2 .2 | ||
argument_pairs= this._sortRequestParams( argument_pairs ); | ||
|
||
// Then concatenate together #3.4.1.3.2 .3 & .4 | ||
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.OAuthEcho.prototype._createSignatureBase= function(method, url, parameters) { | ||
url= this._encodeData( this._normalizeUrl(url) ); | ||
parameters= this._encodeData( parameters ); | ||
return method.toUpperCase() + "&" + url + "&" + parameters; | ||
} | ||
|
||
exports.OAuthEcho.prototype._createSignature= function(signatureBase, tokenSecret) { | ||
if( tokenSecret === undefined ) var tokenSecret= ""; | ||
else tokenSecret= this._encodeData( tokenSecret ); | ||
// consumerSecret is already encoded | ||
var key= this._consumerSecret + "&" + tokenSecret; | ||
|
||
var hash= "" | ||
if( this._signatureMethod == "PLAINTEXT" ) { | ||
hash= this._encodeData(key); | ||
} | ||
else { | ||
if( crypto.Hmac ) { | ||
hash = crypto.createHmac("sha1", key).update(signatureBase).digest("base64"); | ||
} | ||
else { | ||
hash= sha1.HMACSHA1(key, signatureBase); | ||
} | ||
} | ||
return hash; | ||
} | ||
exports.OAuthEcho.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.OAuthEcho.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(''); | ||
} |