1 /** 2 * A library for performing authentication. 3 * Currently supports both Basic and oAuth. 4 */ 5 /** 6 * @constant 7 */ 8 var SPAZCORE_AUTHTYPE_BASIC = 'basic'; 9 /** 10 * @constant 11 */ 12 var SPAZCORE_AUTHTYPE_OAUTH = 'oauth'; 13 14 /** 15 * @constant 16 */ 17 var SPAZAUTH_SERVICES = {}; 18 19 SPAZAUTH_SERVICES[SPAZCORE_ACCOUNT_STATUSNET] = { 20 'authType': SPAZCORE_AUTHTYPE_BASIC 21 }; 22 SPAZAUTH_SERVICES[SPAZCORE_ACCOUNT_TUMBLR_TWITTER] = { 23 'authType': SPAZCORE_AUTHTYPE_BASIC 24 }; 25 SPAZAUTH_SERVICES[SPAZCORE_ACCOUNT_WORDPRESS_TWITTER] = { 26 'authType': SPAZCORE_AUTHTYPE_BASIC 27 }; 28 SPAZAUTH_SERVICES[SPAZCORE_ACCOUNT_IDENTICA] = { 29 'authType': SPAZCORE_AUTHTYPE_BASIC 30 }; 31 SPAZAUTH_SERVICES[SPAZCORE_ACCOUNT_CUSTOM] = { 32 'authType': SPAZCORE_AUTHTYPE_BASIC 33 }; 34 SPAZAUTH_SERVICES['default'] = { 35 'authType': SPAZCORE_AUTHTYPE_BASIC 36 }; 37 38 /** 39 * Construct a new authentication object. 40 * 41 * @param {string} service name of the service to authenticate (ex: twitter, identica) 42 * @class SpazAuth 43 * @constructor 44 */ 45 function SpazAuth(service) { 46 var serviceInfo = SPAZAUTH_SERVICES[service]; 47 if (serviceInfo == undefined) { 48 sch.error("Invalid authentication service: " + service); 49 return null; 50 } 51 52 switch (serviceInfo.authType) { 53 case SPAZCORE_AUTHTYPE_OAUTH: 54 return new SpazOAuth(service, serviceInfo); 55 case SPAZCORE_AUTHTYPE_BASIC: 56 return new SpazBasicAuth(); 57 default: 58 return new SpazBasicAuth(); 59 } 60 }; 61 62 /** 63 * use this to add services that aren't in by default (like, say, stuff with secrets) 64 */ 65 SpazAuth.addService = function(label, opts) { 66 SPAZAUTH_SERVICES[label] = opts; 67 }; 68 69 70 71 /** 72 * Construct a new basic authentication object. 73 * 74 * @class SpazBasicAuth 75 * @constructor 76 */ 77 function SpazBasicAuth() { 78 }; 79 80 /** 81 * Set username and password of account to access service. 82 * 83 * @param {string} username 84 * @param {string} password 85 * @param {function} [onComplete] a callback to fire when complete. Currently just passed TRUE all the time; for compatibility with oAuth need for callbacks 86 * @return {Boolean} true. ALWAYS returns true! 87 */ 88 SpazBasicAuth.prototype.authorize = function(username, password, onComplete) { 89 this.username = username; 90 this.password = password; 91 this.authHeader = "Basic " + sc.helpers.Base64.encode(username + ":" + password); 92 93 if (onComplete) { 94 onComplete.call(this, true); 95 } 96 return true; 97 }; 98 99 100 /** 101 * Returns the authentication header 102 * @returns {string} Authentication header value 103 */ 104 SpazBasicAuth.prototype.signRequest = function() { 105 return this.authHeader; 106 }; 107 108 /** 109 * Load basic auth credentials from a serialized string 110 * 111 * @param {string} pickle the serialized data string returned by save() 112 * @returns {boolean} true if successfully loaded 113 */ 114 SpazBasicAuth.prototype.load = function(pickle) { 115 var credentials = pickle.split(':', 2); 116 if (credentials.length != 2) { 117 sch.error("Invalid basic auth pickle: " + pickle); 118 return false; 119 } 120 121 this.authorize(credentials[0], credentials[1]); 122 return true; 123 }; 124 125 /** 126 * Save basic auth credentials into a serialized string 127 * 128 * @returns {string} serialized string 129 */ 130 SpazBasicAuth.prototype.save = function() { 131 return this.username + ":" + this.password; 132 }; 133 134 135 SpazBasicAuth.prototype.getUsername = function() { 136 return this.username; 137 }; 138 139 SpazBasicAuth.prototype.getPassword = function() { 140 return this.password; 141 }; 142 143 144 /** 145 * Construct a new OAuth authentication object. 146 * 147 * @param {string} realm 148 * @param {object} options 149 * @class SpazOAuth 150 * @constructor 151 */ 152 function SpazOAuth(realm, options) { 153 this.realm = realm; 154 this.opts = options; 155 }; 156 157 /** 158 * Authorize access to the service by fetching an OAuth access token. 159 * 160 * @param {string} username 161 * @param {string} password 162 * @param {function} [onComplete] a callback to fire on complete. If this is set, the request is asynchronous 163 * @returns {boolean} true if authorization successful, otherwise false 164 */ 165 SpazOAuth.prototype.authorize = function(username, password, onComplete) { 166 167 var that = this; 168 169 var async_mode = false; 170 171 this.username = username; 172 173 // Fill in xAuth parameters 174 var parameters = { 175 'x_auth_username': username, 176 'x_auth_password': password, 177 'x_auth_mode': 'client_auth' 178 }; 179 180 // Sign the request 181 OAuth.completeRequest({ 182 method: 'post', 183 action: this.opts.accessURL, 184 parameters: parameters 185 }, this.opts); 186 187 if (onComplete) { 188 async_mode = true; 189 } 190 191 // Perform request to fetch access token 192 var accessToken = null; 193 jQuery.ajax({ 194 async: async_mode, 195 type: 'post', 196 url: this.opts.accessURL, 197 data: parameters, 198 dataType: 'text', 199 success: function(data, textStatus, xhr) { 200 201 sch.error(xhr); 202 203 sch.error("xhr.responseText:" + xhr.responseText); 204 sch.error("xhr.responseXML:" + xhr.responseXML); 205 sch.error('getAllResponseHeaders:n' + xhr.getAllResponseHeaders()); 206 207 208 sch.error("OAuth Data return"); 209 sch.error(sch.enJSON(data)); 210 211 var results = OAuth.decodeForm(data); 212 sch.error("results"); 213 sch.error(sch.enJSON(results)); 214 accessToken = {}; 215 accessToken.key = OAuth.getParameter(results, 'oauth_token'); 216 accessToken.secret = OAuth.getParameter(results, 'oauth_token_secret'); 217 218 that.setAccessToken(accessToken.key, accessToken.secret); 219 220 if (onComplete) { 221 onComplete.call(this, true, accessToken); 222 } 223 224 }, 225 error: function(req, textStatus, error) { 226 sch.error("Failed to fetch oAuth access token: " + req.responseText); 227 228 if (onComplete) { 229 onComplete.call(this, false); 230 } 231 232 }, 233 complete: function(xhr, textStatus) { 234 sch.error('COMPLETE:'); 235 sch.error("xhr.responseText:" + xhr.responseText); 236 sch.error("xhr.responseXML:" + xhr.responseXML); 237 sch.error('getAllResponseHeaders:n' + xhr.getAllResponseHeaders()); 238 239 }, 240 beforeSend: function(xhr) { 241 xhr.setRequestHeader('Accept-Encoding', 'none'); 242 243 } 244 245 }); 246 247 if (async_mode !== true) { 248 if (accessToken != null) { 249 return true; 250 } else { 251 return false; 252 } 253 } else { 254 return null; 255 } 256 // var request = new Ajax.Request(this.opts.accessURL, { 257 // 'asynchronous':true, 258 // 'method':'post', 259 // 'parameters':parameters, 260 // 'onSuccess': function(xhr, foo) { 261 // sch.error('onSuccess====================================================='); 262 // var data = xhr.responseText; 263 // sch.error('foo'); 264 // sch.error(foo); 265 // sch.error(xhr); 266 // 267 // sch.error("xhr.responseText:"+xhr.responseText); 268 // sch.error("xhr.responseXML:"+xhr.responseXML); 269 // sch.error('getAllResponseHeaders:\n'+xhr.getAllResponseHeaders()); 270 // 271 // sch.error("OAuth Data return"); 272 // sch.error(data); 273 // 274 // var results = OAuth.decodeForm(data); 275 // sch.error("results"); 276 // sch.error(sch.enJSON(results)); 277 // accessToken = {}; 278 // accessToken.key = OAuth.getParameter(results, 'oauth_token'); 279 // accessToken.secret = OAuth.getParameter(results, 'oauth_token_secret'); 280 // sch.error('=============================================================='); 281 // if (accessToken != null) { 282 // that.setAccessToken(accessToken.key, accessToken.secret); 283 // onComplete(true); 284 // } else { 285 // onComplete(false); 286 // } 287 // }, 288 // 'onFailure': function(xhr) { 289 // sch.error('onFailure====================================================='); 290 // sch.error("xhr.responseText:"+xhr.responseText); 291 // sch.error('getAllResponseHeaders:\n'+xhr.getAllResponseHeaders()); 292 // sch.error('=============================================================='); 293 // onComplete(false); 294 // } 295 // }); 296 }; 297 298 299 /** 300 * Set the access token 301 * 302 * @param {string} key 303 * @param {string} secret 304 */ 305 SpazOAuth.prototype.setAccessToken = function(key, secret) { 306 this.accessToken = {key: key, secret: secret}; 307 this.signingCredentials = { 308 consumerKey: this.opts.consumerKey, 309 consumerSecret: this.opts.consumerSecret, 310 token: key, 311 tokenSecret: secret 312 }; 313 }; 314 315 /** 316 * Sign a HTTP request and return oAuth header 317 * 318 * @param {string} method HTTP method of the request 319 * @param {string} url the URL of the request 320 * @param {object} parameters map of all parameters in the request 321 * @returns {string} Authorization header value 322 */ 323 SpazOAuth.prototype.signRequest = function(method, url, parameters) { 324 // We need to copy parameters because OAuth.js modifies it. 325 var param = jQuery.extend({}, parameters); 326 327 OAuth.completeRequest({ 328 method: method, 329 action: url, 330 parameters: param 331 }, this.signingCredentials); 332 333 return OAuth.getAuthorizationHeader(this.realm, param); 334 }; 335 336 /** 337 * Load OAuth credentials from a serialized string 338 * 339 * @param {string} pickle the serialized string returned by save() 340 * @returns {boolean} true if successfully loaded 341 */ 342 SpazOAuth.prototype.load = function(pickle) { 343 var credentials = pickle.split(':', 3); 344 if (credentials.length != 3) { 345 sch.error("Invalid oauth pickle: " + pickle); 346 return false; 347 } 348 349 this.username = credentials[0]; 350 this.setAccessToken(credentials[1], credentials[2]); 351 return true; 352 }; 353 354 /** 355 * Save OAuth credentials to a serialized string 356 * 357 * @returns {string} serialized string 358 */ 359 SpazOAuth.prototype.save = function() { 360 return this.username + ":" + this.accessToken.key + ":" + this.accessToken.secret; 361 }; 362 363