source: Dev/trunk/src/client/dojox/io/OAuth.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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