/* OAuthSimple * A simpler version of OAuth * * author: jr conlin * mail: src@anticipatr.com * copyright: unitedHeroes.net * version: 1.0 * url: http://unitedHeroes.net/OAuthSimple * * Copyright (c) 2009, unitedHeroes.net * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the unitedHeroes.net nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY UNITEDHEROES.NET ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UNITEDHEROES.NET BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ var OAuthSimple; if (OAuthSimple === undefined) { /* Simple OAuth * * This class only builds the OAuth elements, it does not do the actual * transmission or reception of the tokens. It does not validate elements * of the token. It is for client use only. * * api_key is the API key, also known as the OAuth consumer key * shared_secret is the shared secret (duh). * * Both the api_key and shared_secret are generally provided by the site * offering OAuth services. You need to specify them at object creation * because nobody ing uses OAuth without that minimal set of * signatures. * * If you want to use the higher order security that comes from the * OAuth token (sorry, I don't provide the functions to fetch that because * sites aren't horribly consistent about how they offer that), you need to * pass those in either with .setTokensAndSecrets() or as an argument to the * .sign() or .getHeaderString() functions. * * Example: var oauthObject = OAuthSimple().sign({path:'http://example.com/rest/', parameters: 'foo=bar&gorp=banana', signatures:{ api_key:'12345abcd', shared_secret:'xyz-5309' }}); document.getElementById('someLink').href=oauthObject.signed_url; * * that will sign as a "GET" using "SHA1-MAC" the url. If you need more than * that, read on, McDuff. */ /** OAuthSimple creator * * Create an instance of OAuthSimple * * @param api_key {string} The API Key (sometimes referred to as the consumer key) This value is usually supplied by the site you wish to use. * @param shared_secret (string) The shared secret. This value is also usually provided by the site you wish to use. */ OAuthSimple = function (consumer_key,shared_secret) { /* if (api_key == undefined) throw("Missing argument: api_key (oauth_consumer_key) for OAuthSimple. This is usually provided by the hosting site."); if (shared_secret == undefined) throw("Missing argument: shared_secret (shared secret) for OAuthSimple. This is usually provided by the hosting site."); */ this._secrets={}; this._parameters={}; // General configuration options. if (consumer_key !== undefined) { this._secrets['consumer_key'] = consumer_key; } if (shared_secret !== undefined) { this._secrets['shared_secret'] = shared_secret; } this._default_signature_method= "HMAC-SHA1"; this._action = "GET"; this._nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; this.reset = function() { this._parameters={}; this._path=undefined; return this; }; /** set the parameters either from a hash or a string * * @param {string,object} List of parameters for the call, this can either be a URI string (e.g. "foo=bar&gorp=banana" or an object/hash) */ this.setParameters = function (parameters) { if (parameters === undefined) { parameters = {}; } if (typeof(parameters) == 'string') { parameters=this._parseParameterString(parameters); } this._parameters = parameters; if (this._parameters['oauth_nonce'] === undefined) { this._getNonce(); } if (this._parameters['oauth_timestamp'] === undefined) { this._getTimestamp(); } if (this._parameters['oauth_method'] === undefined) { this.setSignatureMethod(); } if (this._parameters['oauth_consumer_key'] === undefined) { this._getApiKey(); } if(this._parameters['oauth_token'] === undefined) { this._getAccessToken(); } return this; }; /** convienence method for setParameters * * @param parameters {string,object} See .setParameters */ this.setQueryString = function (parameters) { return this.setParameters(parameters); }; /** Set the target URL (does not include the parameters) * * @param path {string} the fully qualified URI (excluding query arguments) (e.g "http://example.org/foo") */ this.setURL = function (path) { if (path == '') { throw ('No path specified for OAuthSimple.setURL'); } this._path = path; return this; }; /** convienence method for setURL * * @param path {string} see .setURL */ this.setPath = function(path){ return this.setURL(path); }; /** set the "action" for the url, (e.g. GET,POST, DELETE, etc.) * * @param action {string} HTTP Action word. */ this.setAction = function(action) { if (action === undefined) { action="GET"; } action = action.toUpperCase(); if (action.match('[^A-Z]')) { throw ('Invalid action specified for OAuthSimple.setAction'); } this._action = action; return this; }; /** set the signatures (as well as validate the ones you have) * * @param signatures {object} object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token: oauth_secret:} */ this.setTokensAndSecrets = function(signatures) { if (signatures) { for (var i in signatures) { this._secrets[i] = signatures[i]; } } // Aliases if (this._secrets['api_key']) { this._secrets.consumer_key = this._secrets.api_key; } if (this._secrets['access_token']) { this._secrets.oauth_token = this._secrets.access_token; } if (this._secrets['access_secret']) { this._secrets.oauth_secret = this._secrets.access_secret; } // Gauntlet if (this._secrets.consumer_key === undefined) { throw('Missing required consumer_key in OAuthSimple.setTokensAndSecrets'); } if (this._secrets.shared_secret === undefined) { throw('Missing required shared_secret in OAuthSimple.setTokensAndSecrets'); } if ((this._secrets.oauth_token !== undefined) && (this._secrets.oauth_secret === undefined)) { throw('Missing oauth_secret for supplied oauth_token in OAuthSimple.setTokensAndSecrets'); } return this; }; /** set the signature method (currently only Plaintext or SHA-MAC1) * * @param method {string} Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now) */ this.setSignatureMethod = function(method) { if (method === undefined) { method = this._default_signature_method; } //TODO: accept things other than PlainText or SHA-MAC1 if (method.toUpperCase().match(/(PLAINTEXT|HMAC-SHA1)/) === undefined) { throw ('Unknown signing method specified for OAuthSimple.setSignatureMethod'); } this._parameters['oauth_signature_method']= method.toUpperCase(); return this; }; /** sign the request * * note: all arguments are optional, provided you've set them using the * other helper functions. * * @param args {object} hash of arguments for the call * {action:, path:, parameters:, method:, signatures:} * all arguments are optional. */ this.sign = function (args) { if (args === undefined) { args = {}; } // Set any given parameters if(args['action'] !== undefined) { this.setAction(args['action']); } if (args['path'] !== undefined) { this.setPath(args['path']); } if (args['method'] !== undefined) { this.setSignatureMethod(args['method']); } this.setTokensAndSecrets(args['signatures']); if (args['parameters'] !== undefined){ this.setParameters(args['parameters']); } // check the parameters var normParams = this._normalizedParameters(); this._parameters['oauth_signature']=this._generateSignature(normParams); return { parameters: this._parameters, signature: this._oauthEscape(this._parameters['oauth_signature']), signed_url: this._path + '?' + this._normalizedParameters(), header: this.getHeaderString() }; }; /** Return a formatted "header" string * * NOTE: This doesn't set the "Authorization: " prefix, which is required. * I don't set it because various set header functions prefer different * ways to do that. * * @param args {object} see .sign */ this.getHeaderString = function(args) { if (this._parameters['oauth_signature'] === undefined) { this.sign(args); } var result = 'OAuth '; for (var pName in this._parameters) { if (!pName.match(/^oauth/)) { continue; } if ((this._parameters[pName]) instanceof Array) { var pLength = this._parameters[pName].length; for (var j=0;j>16)+(y>>16)+(l>>16);return(m<<16)|(l&0xFFFF);}function _r(n,c){return(n<>>(32-c));}function _c(x,l){x[l>>5]|=0x80<<(24-l%32);x[((l+64>>9)<<4)+15]=l;var w=[80],a=1732584193,b=-271733879,c=-1732584194,d=271733878,e=-1009589776;for(var i=0;i>5]|=(s.charCodeAt(i/8)&m)<<(32-_z-i%32);}return b;}function _h(k,d){var b=_b(k);if(b.length>16){b=_c(b,k.length*_z);}var p=[16],o=[16];for(var i=0;i<16;i++){p[i]=b[i]^0x36363636;o[i]=b[i]^0x5C5C5C5C;}var h=_c(p.concat(_b(d)),512+d.length*_z);return _c(o.concat(h),512+160);}function _n(b){var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s='';for(var i=0;i>2]>>8*(3-i%4))&0xFF)<<16)|(((b[i+1>>2]>>8*(3-(i+1)%4))&0xFF)<<8)|((b[i+2>>2]>>8*(3-(i+2)%4))&0xFF);for(var j=0;j<4;j++){if(i*8+j*6>b.length*32){s+=_p;}else{s+=t.charAt((r>>6*(3-j))&0x3F);}}}return s;}function _x(k,d){return _n(_h(k,d));}return _x(k,d); } this._normalizedParameters = function() { var elements = new Array(); var paramNames = []; var ra =0; for (var paramName in this._parameters) { if (ra++ > 1000) { throw('runaway 1'); } paramNames.unshift(paramName); } paramNames = paramNames.sort(); pLen = paramNames.length; for (var i=0;i 1000) { throw('runaway 1'); } elements.push(this._oauthEscape(paramName) + '=' + this._oauthEscape(sorted[j])); } continue; } elements.push(this._oauthEscape(paramName) + '=' + this._oauthEscape(this._parameters[paramName])); } return elements.join('&'); }; this._generateSignature = function() { var secretKey = this._oauthEscape(this._secrets.shared_secret)+'&'+ this._oauthEscape(this._secrets.oauth_secret); if (this._parameters['oauth_signature_method'] == 'PLAINTEXT') { return secretKey; } if (this._parameters['oauth_signature_method'] == 'HMAC-SHA1') { var sigString = this._oauthEscape(this._action)+'&'+this._oauthEscape(this._path)+'&'+this._oauthEscape(this._normalizedParameters()); return this.b64_hmac_sha1(secretKey,sigString); } return null; }; return this; }; }