OAuth.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. define("dojox/io/OAuth", [
  2. "dojo/_base/kernel", // dojo
  3. "dojo/_base/lang", // mixin
  4. "dojo/_base/array", // isArray, map
  5. "dojo/_base/xhr", // formToObject, queryToObject, xhr
  6. "dojo/dom", // byId
  7. "dojox/encoding/digests/SHA1" // SHA1
  8. ], function(dojo, lang, array, xhr, dom, SHA1){
  9. dojo.getObject("io.OAuth", true, dojox);
  10. dojox.io.OAuth = new (function(){
  11. // summary:
  12. // Helper singleton for signing any kind of Ajax request using the OAuth 1.0 protocol.
  13. // description:
  14. // dojox.io.OAuth is a singleton class designed to allow anyone to sign a request,
  15. // based on the OAuth 1.0 specification, made with any of the Dojo Toolkit's Ajax
  16. // methods (such as dojo.xhr[verb], dojo.io.iframe, etc.).
  17. //
  18. // The main method of dojox.io.OAuth is the sign method (see documentation for .sign);
  19. // the idea is that you will "sign" the kwArgs object you'd normally pass to any of
  20. // the Ajax methods, and then pass the signed object along. As long as the token
  21. // object used is valid (and the client's date and time are synced with a public
  22. // time server), a signed object should be passed along correctly.
  23. //
  24. // dojox.io.OAuth does not deal with the OAuth handshake process at all.
  25. //
  26. // This object was developed against the Netflix API (OAuth-based service); see
  27. // http://developer.netflix.com for more details.
  28. var encode = this.encode = function(s){
  29. if(!("" + s).length){ return ""; }
  30. return encodeURIComponent(s)
  31. .replace(/\!/g, "%21")
  32. .replace(/\*/g, "%2A")
  33. .replace(/\'/g, "%27")
  34. .replace(/\(/g, "%28")
  35. .replace(/\)/g, "%29");
  36. };
  37. var decode = this.decode = function(str){
  38. // summary:
  39. // Break apart the passed string and decode.
  40. // Some special cases are handled.
  41. var a=[], list=str.split("&");
  42. for(var i=0, l=list.length; i<l; i++){
  43. var item=list[i];
  44. if(list[i]==""){ continue; } // skip this one.
  45. if(list[i].indexOf("=")>-1){
  46. var tmp=list[i].split("=");
  47. a.push([ decodeURIComponent(tmp[0]), decodeURIComponent(tmp[1]) ]);
  48. } else {
  49. a.push([ decodeURIComponent(list[i]), null ]);
  50. }
  51. }
  52. return a;
  53. };
  54. function parseUrl(url){
  55. // summary:
  56. // Create a map out of the passed URL. Need to pull any
  57. // query string parameters off the URL for the base signature string.
  58. var keys = [
  59. "source","protocol","authority","userInfo",
  60. "user","password","host","port",
  61. "relative","path","directory",
  62. "file","query","anchor"
  63. ],
  64. parser=/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
  65. match=parser.exec(url),
  66. map = {},
  67. i=keys.length;
  68. // create the base map first.
  69. while(i--){ map[keys[i]] = match[i] || ""; }
  70. // create the normalized version of the url and add it to the map
  71. var p=map.protocol.toLowerCase(),
  72. a=map.authority.toLowerCase(),
  73. b=(p=="http"&&map.port==80)||(p=="https"&&map.port==443);
  74. if(b){
  75. if(a.lastIndexOf(":")>-1){
  76. a=a.substring(0, a.lastIndexOf(":"));
  77. }
  78. }
  79. var path=map.path||"/";
  80. map.url=p+"://"+a+path;
  81. // return the map
  82. return map;
  83. }
  84. var tab="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
  85. function nonce(length){
  86. var s="", tl=tab.length;
  87. for(var i=0; i<length; i++){
  88. s+=tab.charAt(Math.floor(Math.random()*tl));
  89. }
  90. return s;
  91. }
  92. function timestamp(){
  93. return Math.floor(new Date().valueOf()/1000)-2;
  94. }
  95. function signature(data, key, type){
  96. if(type && type!="PLAINTEXT" && type!="HMAC-SHA1"){
  97. throw new Error("dojox.io.OAuth: the only supported signature encodings are PLAINTEXT and HMAC-SHA1.");
  98. }
  99. if(type=="PLAINTEXT"){
  100. return key;
  101. } else {
  102. // assume SHA1 HMAC
  103. return SHA1._hmac(data, key);
  104. }
  105. }
  106. function key(args){
  107. // summary:
  108. // return the key used to sign a message based on the token object.
  109. return encode(args.consumer.secret)
  110. + "&"
  111. + (args.token && args.token.secret ? encode(args.token.secret) : "");
  112. }
  113. function addOAuth(/* dojo.__XhrArgs */args, /* dojox.io.__OAuthArgs */oaa){
  114. // summary:
  115. // Add the OAuth parameters to the query string/content.
  116. var o = {
  117. oauth_consumer_key: oaa.consumer.key,
  118. oauth_nonce: nonce(16),
  119. oauth_signature_method: oaa.sig_method || "HMAC-SHA1",
  120. oauth_timestamp: timestamp(),
  121. oauth_version: "1.0"
  122. }
  123. if(oaa.token){
  124. o.oauth_token = oaa.token.key;
  125. }
  126. args.content = dojo.mixin(args.content||{}, o);
  127. }
  128. function convertArgs(args){
  129. // summary:
  130. // Because of the need to create a base string, we have to do
  131. // some manual args preparation instead of relying on the internal
  132. // Dojo xhr functions. But we'll let dojo.xhr assemble things
  133. // as it normally would.
  134. var miArgs = [{}], formObject;
  135. if(args.form){
  136. if(!args.content){ args.content = {}; }
  137. var form = dojo.byId(args.form);
  138. var actnNode = form.getAttributeNode("action");
  139. args.url = args.url || (actnNode ? actnNode.value : null);
  140. formObject = dojo.formToObject(form);
  141. delete args.form;
  142. }
  143. if(formObject){ miArgs.push(formObject); }
  144. if(args.content){ miArgs.push(args.content); }
  145. // pull anything off the query string
  146. var map = parseUrl(args.url);
  147. if(map.query){
  148. var tmp = dojo.queryToObject(map.query);
  149. // re-encode the values. sigh
  150. for(var p in tmp){ tmp[p] = encodeURIComponent(tmp[p]); }
  151. miArgs.push(tmp);
  152. }
  153. args._url = map.url;
  154. // now set up all the parameters as an array of 2 element arrays.
  155. var a = [];
  156. for(var i=0, l=miArgs.length; i<l; i++){
  157. var item=miArgs[i];
  158. for(var p in item){
  159. if(dojo.isArray(item[p])){
  160. // handle multiple values
  161. for(var j=0, jl=item.length; j<jl; j++){
  162. a.push([ p, item[j] ]);
  163. }
  164. } else {
  165. a.push([ p, item[p] ]);
  166. }
  167. }
  168. }
  169. args._parameters = a;
  170. return args;
  171. }
  172. function baseString(/* String */method, /* dojo.__XhrArgs */args, /* dojox.io.__OAuthArgs */oaa){
  173. // create and return the base string out of the args.
  174. addOAuth(args, oaa);
  175. convertArgs(args);
  176. var a = args._parameters;
  177. // sort the parameters
  178. a.sort(function(a,b){
  179. if(a[0]>b[0]){ return 1; }
  180. if(a[0]<b[0]){ return -1; }
  181. if(a[1]>b[1]){ return 1; }
  182. if(a[1]<b[1]){ return -1; }
  183. return 0;
  184. });
  185. // encode.
  186. var s = dojo.map(a, function(item){
  187. return encode(item[0]) + "=" + encode((""+item[1]).length ? item[1] : "");
  188. }).join("&");
  189. var baseString = method.toUpperCase()
  190. + "&" + encode(args._url)
  191. + "&" + encode(s);
  192. return baseString;
  193. }
  194. function sign(method, args, oaa){
  195. // return the oauth_signature for this message.
  196. var k = key(oaa),
  197. message = baseString(method, args, oaa),
  198. s = signature(message, k, oaa.sig_method || "HMAC-SHA1");
  199. args.content["oauth_signature"] = s;
  200. return args;
  201. }
  202. /*=====
  203. dojox.io.OAuth.__AccessorArgs = function(key, secret){
  204. // key: String
  205. // The key or token issued to either the consumer or by the OAuth service.
  206. // secret: String
  207. // The secret (shared secret for consumers, issued secret by OAuth service).
  208. this.key = key;
  209. this.secret = secret;
  210. };
  211. dojox.io.OAuth.__OAuthArgs = function(consumer, sig_method, token){
  212. // consumer: dojox.io.OAuth.__AccessorArgs
  213. // The consumer information issued to your OpenAuth application.
  214. // sig_method: String
  215. // The method used to create the signature. Should be PLAINTEXT or HMAC-SHA1.
  216. // token: dojox.io.OAuth.__AccessorArgs?
  217. // The request token and secret issued by the OAuth service. If not
  218. // issued yet, this should be null.
  219. this.consumer = consumer;
  220. this.token = token;
  221. }
  222. =====*/
  223. /*
  224. * Process goes something like this:
  225. * 1. prepare the base string
  226. * 2. create the key
  227. * 3. create the signature based on the base string and the key
  228. * 4. send the request using dojo.xhr[METHOD].
  229. */
  230. this.sign = function(/* String*/method, /* dojo.__XhrArgs */args, /* dojox.io.OAuth.__OAuthArgs */oaa){
  231. // summary:
  232. // Given the OAuth access arguments, sign the kwArgs that you would pass
  233. // to any dojo Ajax method (dojo.xhr*, dojo.io.iframe, dojo.io.script).
  234. // example:
  235. // Sign the kwArgs object for use with dojo.xhrGet:
  236. // | var oaa = {
  237. // | consumer: {
  238. // | key: "foobar",
  239. // | secret: "barbaz"
  240. // | }
  241. // | };
  242. // |
  243. // | var args = dojox.io.OAuth.sign("GET", myAjaxKwArgs, oaa);
  244. // | dojo.xhrGet(args);
  245. return sign(method, args, oaa);
  246. };
  247. // TODO: handle redirect requests?
  248. this.xhr = function(/* String */method, /* dojo.__XhrArgs */args, /* dojox.io.OAuth.__OAuthArgs */oaa, /* Boolean? */hasBody){
  249. /* summary:
  250. * Make an XHR request that is OAuth signed.
  251. * example:
  252. * | var dfd = dojox.io.OAuth.xhrGet({
  253. * | url: "http://someauthdomain.com/path?foo=bar",
  254. * | load: function(response, ioArgs){ }
  255. * | },
  256. * | {
  257. * | consumer:{ key: "lasdkf9asdnfsdf", secret: "9asdnfskdfysjr" }
  258. * | });
  259. */
  260. sign(method, args, oaa);
  261. return xhr(method, args, hasBody);
  262. };
  263. this.xhrGet = function(/* dojo.__XhrArgs */args, /* dojox.io.OAuth.__OAuthArgs*/ oaa){
  264. return this.xhr("GET", args, oaa);
  265. };
  266. this.xhrPost = this.xhrRawPost = function(/* dojo.__XhrArgs */args, /* dojox.io.OAuth.__OAuthArgs*/ oaa){
  267. return this.xhr("POST", args, oaa, true);
  268. };
  269. this.xhrPut = this.xhrRawPut = function(/* dojo.__XhrArgs */args, /* dojox.io.OAuth.__OAuthArgs*/ oaa){
  270. return this.xhr("PUT", args, oaa, true);
  271. };
  272. this.xhrDelete = function(/* dojo.__XhrArgs */args, /* dojox.io.OAuth.__OAuthArgs*/ oaa){
  273. return this.xhr("DELETE", args, oaa);
  274. };
  275. })();
  276. return dojox.io.OAuth;
  277. });