var crypto= require('crypto'), sha1= require('./sha1'), http= require('http'), https= require('https'), URL= require('url'), querystring= require('querystring'), OAuthUtils= require('./_utils'); exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, version, authorize_callback, signatureMethod, nonceSize, customHeaders) { this._isEcho = false; this._requestUrl= requestUrl; this._accessUrl= accessUrl; this._consumerKey= consumerKey; this._consumerSecret= this._encodeData( consumerSecret ); if (signatureMethod == "RSA-SHA1") { this._privateKey = consumerSecret; } this._version= version; if( authorize_callback === undefined ) { this._authorize_callback= "oob"; } else { this._authorize_callback= authorize_callback; } if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-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"} this._clientOptions= this._defaultClientOptions= {"requestTokenHttpMethod": "POST", "accessTokenHttpMethod": "POST", "followRedirects": true}; this._oauthParameterSeperator = ","; }; exports.OAuthEcho= function(realm, verify_credentials, consumerKey, consumerSecret, version, signatureMethod, nonceSize, customHeaders) { this._isEcho = true; this._realm= realm; this._verifyCredentials = verify_credentials; this._consumerKey= consumerKey; this._consumerSecret= this._encodeData( consumerSecret ); if (signatureMethod == "RSA-SHA1") { this._privateKey = consumerSecret; } this._version= version; if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1" && signatureMethod != "RSA-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"}; this._oauthParameterSeperator = ","; } exports.OAuthEcho.prototype = exports.OAuth.prototype; 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 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.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._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; } // Is the parameter considered an OAuth parameter exports.OAuth.prototype._isParameterNameAnOAuthParameter= function(parameter) { var m = parameter.match('^oauth_'); if( m && ( m[0] === "oauth_" ) ) { return true; } else { return false; } }; // build the OAuth request authorization header exports.OAuth.prototype._buildAuthorizationHeaders= function(orderedParameters) { var authHeader="OAuth "; if( this._isEcho ) { authHeader += '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])+"\""+ this._oauthParameterSeperator; } } authHeader= authHeader.substring(0, authHeader.length-this._oauthParameterSeperator.length); return authHeader; } // Takes an object literal that represents the arguments, and returns an array // of argument/value pairs. exports.OAuth.prototype._makeArrayOfArgumentsHash= function(argumentsHash) { var argument_pairs= []; for(var key in argumentsHash ) { if (argumentsHash.hasOwnProperty(key)) { var value= argumentsHash[key]; if( Array.isArray(value) ) { for(var i=0;i= 200 && response.statusCode <= 299 ) { callback(null, data, response); } else { // Follow 301 or 302 redirects with Location HTTP header if((response.statusCode == 301 || response.statusCode == 302) && clientOptions.followRedirects && 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); } } } } request.on('response', function (response) { response.setEncoding('utf8'); response.on('data', function (chunk) { data+=chunk; }); response.on('end', function () { passBackControl( response ); }); response.on('close', function () { if( allowEarlyClose ) { passBackControl( response ); } }); }); request.on("error", function(err) { if(!callbackCalled) { callbackCalled= true; callback( err ) } }); 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.OAuth.prototype.setClientOptions= function(options) { var key, mergedOptions= {}, hasOwnProperty= Object.prototype.hasOwnProperty; for( key in this._defaultClientOptions ) { if( !hasOwnProperty.call(options, key) ) { mergedOptions[key]= this._defaultClientOptions[key]; } else { mergedOptions[key]= options[key]; } } this._clientOptions= mergedOptions; }; exports.OAuth.prototype.getOAuthAccessToken= function(oauth_token, oauth_token_secret, oauth_verifier, callback) { var extraParams= {}; if( typeof oauth_verifier == "function" ) { callback= oauth_verifier; } else { extraParams.oauth_verifier= oauth_verifier; } this._performSecureRequest( oauth_token, oauth_token_secret, this._clientOptions.accessTokenHttpMethod, this._accessUrl, extraParams, null, null, function(error, data, response) { if( error ) callback(error); else { var results= querystring.parse( data ); var oauth_access_token= results["oauth_token"]; delete results["oauth_token"]; var oauth_access_token_secret= results["oauth_token_secret"]; delete results["oauth_token_secret"]; callback(null, oauth_access_token, oauth_access_token_secret, results ); } }) } // Deprecated exports.OAuth.prototype.getProtectedResource= function(url, method, oauth_token, oauth_token_secret, callback) { this._performSecureRequest( oauth_token, oauth_token_secret, method, url, null, "", null, callback ); } exports.OAuth.prototype.delete= function(url, oauth_token, oauth_token_secret, callback) { return this._performSecureRequest( oauth_token, oauth_token_secret, "DELETE", url, null, "", null, callback ); } exports.OAuth.prototype.get= function(url, oauth_token, oauth_token_secret, callback) { return this._performSecureRequest( oauth_token, oauth_token_secret, "GET", url, null, "", null, callback ); } exports.OAuth.prototype._putOrPost= function(method, url, oauth_token, oauth_token_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" && !Buffer.isBuffer(post_body) ) { post_content_type= "application/x-www-form-urlencoded" extra_params= post_body; post_body= null; } return this._performSecureRequest( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type, callback ); } exports.OAuth.prototype.put= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) { return this._putOrPost("PUT", url, oauth_token, oauth_token_secret, post_body, post_content_type, callback); } exports.OAuth.prototype.post= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) { return this._putOrPost("POST", url, oauth_token, oauth_token_secret, post_body, post_content_type, callback); } /** * Gets a request token from the OAuth provider and passes that information back * to the calling code. * * The callback should expect a function of the following form: * * function(err, token, token_secret, parsedQueryString) {} * * This method has optional parameters so can be called in the following 2 ways: * * 1) Primary use case: Does a basic request with no extra parameters * getOAuthRequestToken( callbackFunction ) * * 2) As above but allows for provision of extra parameters to be sent as part of the query to the server. * getOAuthRequestToken( extraParams, callbackFunction ) * * N.B. This method will HTTP POST verbs by default, if you wish to override this behaviour you will * need to provide a requestTokenHttpMethod option when creating the client. * **/ exports.OAuth.prototype.getOAuthRequestToken= function( extraParams, callback ) { if( typeof extraParams == "function" ){ callback = extraParams; extraParams = {}; } // Callbacks are 1.0A related if( this._authorize_callback ) { extraParams["oauth_callback"]= this._authorize_callback; } this._performSecureRequest( null, null, this._clientOptions.requestTokenHttpMethod, this._requestUrl, extraParams, null, null, function(error, data, response) { if( error ) callback(error); 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 ); } }); } exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret, method) { if( method === undefined ) { var method= "GET"; } var orderedParameters= this._prepareParameters(oauth_token, oauth_token_secret, method, url, {}); var parsedUrl= URL.parse( url, false ); var query=""; for( var i= 0 ; i < orderedParameters.length; i++) { query+= orderedParameters[i][0]+"="+ this._encodeData(orderedParameters[i][1]) + "&"; } query= query.substring(0, query.length-1); return parsedUrl.protocol + "//"+ parsedUrl.host + parsedUrl.pathname + "?" + query; }; exports.OAuth.prototype.authHeader= function(url, oauth_token, oauth_token_secret, method) { if( method === undefined ) { var method= "GET"; } var orderedParameters= this._prepareParameters(oauth_token, oauth_token_secret, method, url, {}); return this._buildAuthorizationHeaders(orderedParameters); };