root / dojox / trunk / io / OAuth.js

Revision 20375, 9.0 kB (checked in by ttrenka, 11 months ago)

Implement patch as submitted by Mark Wubben. Fixes #9313 !strict.

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