Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Authenticator class wraps the communications between Gaia and its host. | 6 * Authenticator class wraps the communications between Gaia and its host. |
| 7 */ | 7 */ |
| 8 function Authenticator() { | 8 function Authenticator() { |
| 9 } | 9 } |
| 10 | 10 |
| 11 /** | 11 /** |
| 12 * Gaia auth extension url origin. | 12 * Gaia auth extension url origin. |
| 13 * @type {string} | 13 * @type {string} |
| 14 */ | 14 */ |
| 15 Authenticator.THIS_EXTENSION_ORIGIN = | 15 Authenticator.THIS_EXTENSION_ORIGIN = |
| 16 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik'; | 16 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik'; |
| 17 | 17 |
| 18 /** | 18 /** |
| 19 * The lowest version of the credentials passing API supported. | |
| 20 * @type {number} | |
| 21 */ | |
| 22 Authenticator.MIN_API_VERSION_VERSION = 1; | |
| 23 | |
| 24 /** | |
| 25 * The highest version of the credentials passing API supported. | |
| 26 * @type {number} | |
| 27 */ | |
| 28 Authenticator.MAX_API_VERSION_VERSION = 2; | |
| 29 | |
| 30 /** | |
| 31 * The key types supported for credentials passing API 2 and higher. | |
| 32 * @type {Array} Array of strings. | |
| 33 */ | |
| 34 Authenticator.API_KEY_TYPES = [ | |
| 35 'KEY_TYPE_PASSWORD_PLAIN', | |
|
Jorge Lucangeli Obes
2014/04/30 22:31:37
Is this equivalent of what we're doing in v1?
bartfab (slow)
2014/05/01 22:47:48
Yes. For now, both v1 and v2 expect the password i
Jorge Lucangeli Obes
2014/05/01 23:00:24
IIUC the password is transmitted in plaintext, but
bartfab (slow)
2014/05/02 06:29:53
The password is sent to two places:
1) The SAML I
| |
| 36 ]; | |
| 37 | |
| 38 /** | |
| 19 * Singleton getter of Authenticator. | 39 * Singleton getter of Authenticator. |
| 20 * @return {Object} The singleton instance of Authenticator. | 40 * @return {Object} The singleton instance of Authenticator. |
| 21 */ | 41 */ |
| 22 Authenticator.getInstance = function() { | 42 Authenticator.getInstance = function() { |
| 23 if (!Authenticator.instance_) { | 43 if (!Authenticator.instance_) { |
| 24 Authenticator.instance_ = new Authenticator(); | 44 Authenticator.instance_ = new Authenticator(); |
| 25 } | 45 } |
| 26 return Authenticator.instance_; | 46 return Authenticator.instance_; |
| 27 }; | 47 }; |
| 28 | 48 |
| 29 Authenticator.prototype = { | 49 Authenticator.prototype = { |
| 30 email_: null, | 50 email_: null, |
| 31 password_: null, | 51 passwordBytes_: null, |
| 32 attemptToken_: null, | 52 attemptToken_: null, |
| 33 | 53 |
| 34 // Input params from extension initialization URL. | 54 // Input params from extension initialization URL. |
| 35 inputLang_: undefined, | 55 inputLang_: undefined, |
| 36 intputEmail_: undefined, | 56 intputEmail_: undefined, |
| 37 | 57 |
| 38 isSAMLFlow_: false, | 58 isSAMLFlow_: false, |
| 39 isSAMLEnabled_: false, | 59 isSAMLEnabled_: false, |
| 40 supportChannel_: null, | 60 supportChannel_: null, |
| 41 | 61 |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 168 }, | 188 }, |
| 169 | 189 |
| 170 /** | 190 /** |
| 171 * Invoked when the signin flow is complete. | 191 * Invoked when the signin flow is complete. |
| 172 * @param {Object=} opt_extraMsg Optional extra info to send. | 192 * @param {Object=} opt_extraMsg Optional extra info to send. |
| 173 */ | 193 */ |
| 174 completeLogin_: function(opt_extraMsg) { | 194 completeLogin_: function(opt_extraMsg) { |
| 175 var msg = { | 195 var msg = { |
| 176 'method': 'completeLogin', | 196 'method': 'completeLogin', |
| 177 'email': (opt_extraMsg && opt_extraMsg.email) || this.email_, | 197 'email': (opt_extraMsg && opt_extraMsg.email) || this.email_, |
| 178 'password': (opt_extraMsg && opt_extraMsg.password) || this.password_, | 198 'password': (opt_extraMsg && opt_extraMsg.password) || |
| 199 this.passwordBytes_, | |
| 179 'usingSAML': this.isSAMLFlow_, | 200 'usingSAML': this.isSAMLFlow_, |
| 180 'chooseWhatToSync': this.chooseWhatToSync_ || false, | 201 'chooseWhatToSync': this.chooseWhatToSync_ || false, |
| 181 'skipForNow': opt_extraMsg && opt_extraMsg.skipForNow, | 202 'skipForNow': opt_extraMsg && opt_extraMsg.skipForNow, |
| 182 'sessionIndex': opt_extraMsg && opt_extraMsg.sessionIndex | 203 'sessionIndex': opt_extraMsg && opt_extraMsg.sessionIndex |
| 183 }; | 204 }; |
| 184 window.parent.postMessage(msg, this.parentPage_); | 205 window.parent.postMessage(msg, this.parentPage_); |
| 185 if (this.isSAMLEnabled_) | 206 if (this.isSAMLEnabled_) |
| 186 this.supportChannel_.send({name: 'resetAuth'}); | 207 this.supportChannel_.send({name: 'resetAuth'}); |
| 187 }, | 208 }, |
| 188 | 209 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 224 /** | 245 /** |
| 225 * Invoked when the background page sends 'onHostedPageLoaded' message. | 246 * Invoked when the background page sends 'onHostedPageLoaded' message. |
| 226 * @param {!Object} msg Details sent with the message. | 247 * @param {!Object} msg Details sent with the message. |
| 227 */ | 248 */ |
| 228 onAuthPageLoaded_: function(msg) { | 249 onAuthPageLoaded_: function(msg) { |
| 229 var isSAMLPage = msg.url.indexOf(this.gaiaUrl_) != 0; | 250 var isSAMLPage = msg.url.indexOf(this.gaiaUrl_) != 0; |
| 230 | 251 |
| 231 if (isSAMLPage && !this.isSAMLFlow_) { | 252 if (isSAMLPage && !this.isSAMLFlow_) { |
| 232 // GAIA redirected to a SAML login page. The credentials provided to this | 253 // GAIA redirected to a SAML login page. The credentials provided to this |
| 233 // page will determine what user gets logged in. The credentials obtained | 254 // page will determine what user gets logged in. The credentials obtained |
| 234 // from the GAIA login from are no longer relevant and can be discarded. | 255 // from the GAIA login form are no longer relevant and can be discarded. |
| 235 this.isSAMLFlow_ = true; | 256 this.isSAMLFlow_ = true; |
| 236 this.email_ = null; | 257 this.email_ = null; |
| 237 this.password_ = null; | 258 this.passwordBytes_ = null; |
| 238 } | 259 } |
| 239 | 260 |
| 240 window.parent.postMessage({ | 261 window.parent.postMessage({ |
| 241 'method': 'authPageLoaded', | 262 'method': 'authPageLoaded', |
| 242 'isSAML': this.isSAMLFlow_, | 263 'isSAML': this.isSAMLFlow_, |
| 243 'domain': extractDomain(msg.url) | 264 'domain': extractDomain(msg.url) |
| 244 }, this.parentPage_); | 265 }, this.parentPage_); |
| 245 }, | 266 }, |
| 246 | 267 |
| 247 /** | 268 /** |
| 248 * Invoked when the background page sends an 'onInsecureContentBlocked' | 269 * Invoked when the background page sends an 'onInsecureContentBlocked' |
| 249 * message. | 270 * message. |
| 250 */ | 271 */ |
| 251 onInsecureContentBlocked_: function() { | 272 onInsecureContentBlocked_: function() { |
| 252 window.parent.postMessage({'method': 'insecureContentBlocked'}, | 273 window.parent.postMessage({'method': 'insecureContentBlocked'}, |
| 253 this.parentPage_); | 274 this.parentPage_); |
| 254 }, | 275 }, |
| 255 | 276 |
| 256 /** | 277 /** |
| 257 * Invoked when one of the credential passing API methods is called by a SAML | 278 * Invoked when one of the credential passing API methods is called by a SAML |
| 258 * provider. | 279 * provider. |
| 259 * @param {!Object} msg Details of the API call. | 280 * @param {!Object} msg Details of the API call. |
| 260 */ | 281 */ |
| 261 onAPICall_: function(msg) { | 282 onAPICall_: function(msg) { |
| 262 var call = msg.call; | 283 var call = msg.call; |
| 284 if (call.method == 'initialize') { | |
| 285 // TODO(bartfab): There was no |requestedVersion| parameter in version 1 | |
| 286 // of the API. Remove this code once all consumers have switched to | |
| 287 // version 2 or higher. | |
| 288 if (!call.hasOwnProperty('requestedVersion')) { | |
| 289 if (Authenticator.MIN_API_VERSION_VERSION == 1) { | |
| 290 this.apiVersion_ = 1; | |
| 291 this.initialized_ = true; | |
| 292 this.sendInitializationSuccess_(); | |
| 293 } | |
| 294 // The glue code for API version 1 interprets all responses as success. | |
| 295 // Instead of reporting failure, do not send any response at all. | |
| 296 return; | |
| 297 } | |
| 298 | |
| 299 if (!Number.isInteger(call.requestedVersion) || | |
| 300 call.requestedVersion < Authenticator.MIN_API_VERSION_VERSION) { | |
| 301 this.sendInitializationFailure_(); | |
| 302 return; | |
| 303 } | |
| 304 | |
| 305 this.apiVersion_ = Math.min(call.requested_version, | |
| 306 Authenticator.MAX_API_VERSION_VERSION); | |
| 307 this.initialized_ = true; | |
| 308 this.sendInitializationSuccess_(); | |
| 309 return; | |
| 310 } | |
| 311 | |
| 263 if (call.method == 'add') { | 312 if (call.method == 'add') { |
| 313 if (this.apiVersion_ > 1 && | |
| 314 Authenticator.API_KEY_TYPES.indexOf(call.keyType) == -1) { | |
| 315 console.error('Authenticator.onAPICall_: unsupported key type'); | |
| 316 return; | |
| 317 } | |
| 264 this.apiToken_ = call.token; | 318 this.apiToken_ = call.token; |
| 265 this.email_ = call.user; | 319 this.email_ = call.user; |
| 266 this.password_ = call.password; | 320 if (this.apiVersion_ == 1) |
| 321 this.passwordBytes_ = call.password; | |
| 322 else | |
| 323 this.passwordBytes_ = call.passwordBytes; | |
| 267 } else if (call.method == 'confirm') { | 324 } else if (call.method == 'confirm') { |
| 268 if (call.token != this.apiToken_) | 325 if (call.token != this.apiToken_) |
| 269 console.error('Authenticator.onAPICall_: token mismatch'); | 326 console.error('Authenticator.onAPICall_: token mismatch'); |
| 270 } else { | 327 } else { |
| 271 console.error('Authenticator.onAPICall_: unknown message'); | 328 console.error('Authenticator.onAPICall_: unknown message'); |
| 272 } | 329 } |
| 273 }, | 330 }, |
| 274 | 331 |
| 332 sendInitializationSuccess_: function() { | |
| 333 var response = { | |
| 334 result: 'initialized', | |
| 335 version: this.apiVersion_ | |
| 336 }; | |
| 337 if (this.apiVersion_ >= 2) | |
| 338 response['keyTypes'] = Authenticator.API_KEY_TYPES; | |
| 339 | |
| 340 this.supportChannel_.send({name: 'apiResponse', response: response}); | |
| 341 }, | |
| 342 | |
| 343 sendInitializationFailure_: function() { | |
| 344 this.supportChannel_.send({ | |
| 345 name: 'apiResponse', | |
| 346 response: {result: 'initialization_failed'} | |
| 347 }); | |
| 348 }, | |
| 349 | |
| 275 onConfirmLogin_: function() { | 350 onConfirmLogin_: function() { |
| 276 if (!this.isSAMLFlow_) { | 351 if (!this.isSAMLFlow_) { |
| 277 this.completeLogin_(); | 352 this.completeLogin_(); |
| 278 return; | 353 return; |
| 279 } | 354 } |
| 280 | 355 |
| 281 var apiUsed = !!this.password_; | 356 var apiUsed = !!this.passwordBytes_; |
| 282 | 357 |
| 283 // Retrieve the e-mail address of the user who just authenticated from GAIA. | 358 // Retrieve the e-mail address of the user who just authenticated from GAIA. |
| 284 window.parent.postMessage({method: 'retrieveAuthenticatedUserEmail', | 359 window.parent.postMessage({method: 'retrieveAuthenticatedUserEmail', |
| 285 attemptToken: this.attemptToken_, | 360 attemptToken: this.attemptToken_, |
| 286 apiUsed: apiUsed}, | 361 apiUsed: apiUsed}, |
| 287 this.parentPage_); | 362 this.parentPage_); |
| 288 | 363 |
| 289 if (!apiUsed) { | 364 if (!apiUsed) { |
| 290 this.supportChannel_.sendWithCallback( | 365 this.supportChannel_.sendWithCallback( |
| 291 {name: 'getScrapedPasswords'}, | 366 {name: 'getScrapedPasswords'}, |
| 292 function(passwords) { | 367 function(passwords) { |
| 293 if (passwords.length == 0) { | 368 if (passwords.length == 0) { |
| 294 window.parent.postMessage( | 369 window.parent.postMessage( |
| 295 {method: 'noPassword', email: this.email_}, | 370 {method: 'noPassword', email: this.email_}, |
| 296 this.parentPage_); | 371 this.parentPage_); |
| 297 } else { | 372 } else { |
| 298 window.parent.postMessage({method: 'confirmPassword', | 373 window.parent.postMessage({method: 'confirmPassword', |
| 299 email: this.email_, | 374 email: this.email_, |
| 300 passwordCount: passwords.length}, | 375 passwordCount: passwords.length}, |
| 301 this.parentPage_); | 376 this.parentPage_); |
| 302 } | 377 } |
| 303 }.bind(this)); | 378 }.bind(this)); |
| 304 } | 379 } |
| 305 }, | 380 }, |
| 306 | 381 |
| 307 maybeCompleteSAMLLogin_: function() { | 382 maybeCompleteSAMLLogin_: function() { |
| 308 // SAML login is complete when the user's e-mail address has been retrieved | 383 // SAML login is complete when the user's e-mail address has been retrieved |
| 309 // from GAIA and the user has successfully confirmed the password. | 384 // from GAIA and the user has successfully confirmed the password. |
| 310 if (this.email_ !== null && this.password_ !== null) | 385 if (this.email_ !== null && this.passwordBytes_ !== null) |
| 311 this.completeLogin_(); | 386 this.completeLogin_(); |
| 312 }, | 387 }, |
| 313 | 388 |
| 314 onVerifyConfirmedPassword_: function(password) { | 389 onVerifyConfirmedPassword_: function(password) { |
| 315 this.supportChannel_.sendWithCallback( | 390 this.supportChannel_.sendWithCallback( |
| 316 {name: 'getScrapedPasswords'}, | 391 {name: 'getScrapedPasswords'}, |
| 317 function(passwords) { | 392 function(passwords) { |
| 318 for (var i = 0; i < passwords.length; ++i) { | 393 for (var i = 0; i < passwords.length; ++i) { |
| 319 if (passwords[i] == password) { | 394 if (passwords[i] == password) { |
| 320 this.password_ = passwords[i]; | 395 this.passwordBytes_ = passwords[i]; |
| 321 this.maybeCompleteSAMLLogin_(); | 396 this.maybeCompleteSAMLLogin_(); |
| 322 return; | 397 return; |
| 323 } | 398 } |
| 324 } | 399 } |
| 325 window.parent.postMessage( | 400 window.parent.postMessage( |
| 326 {method: 'confirmPassword', email: this.email_}, | 401 {method: 'confirmPassword', email: this.email_}, |
| 327 this.parentPage_); | 402 this.parentPage_); |
| 328 }.bind(this)); | 403 }.bind(this)); |
| 329 }, | 404 }, |
| 330 | 405 |
| 331 onMessage: function(e) { | 406 onMessage: function(e) { |
| 332 var msg = e.data; | 407 var msg = e.data; |
| 333 if (msg.method == 'attemptLogin' && this.isGaiaMessage_(e)) { | 408 if (msg.method == 'attemptLogin' && this.isGaiaMessage_(e)) { |
| 334 this.email_ = msg.email; | 409 this.email_ = msg.email; |
| 335 this.password_ = msg.password; | 410 this.passwordBytes_ = msg.password; |
| 336 this.attemptToken_ = msg.attemptToken; | 411 this.attemptToken_ = msg.attemptToken; |
| 337 this.chooseWhatToSync_ = msg.chooseWhatToSync; | 412 this.chooseWhatToSync_ = msg.chooseWhatToSync; |
| 338 this.isSAMLFlow_ = false; | 413 this.isSAMLFlow_ = false; |
| 339 if (this.isSAMLEnabled_) | 414 if (this.isSAMLEnabled_) |
| 340 this.supportChannel_.send({name: 'startAuth'}); | 415 this.supportChannel_.send({name: 'startAuth'}); |
| 341 } else if (msg.method == 'clearOldAttempts' && this.isGaiaMessage_(e)) { | 416 } else if (msg.method == 'clearOldAttempts' && this.isGaiaMessage_(e)) { |
| 342 this.email_ = null; | 417 this.email_ = null; |
| 343 this.password_ = null; | 418 this.passwordBytes_ = null; |
| 344 this.attemptToken_ = null; | 419 this.attemptToken_ = null; |
| 345 this.isSAMLFlow_ = false; | 420 this.isSAMLFlow_ = false; |
| 346 this.onLoginUILoaded_(); | 421 this.onLoginUILoaded_(); |
| 347 if (this.isSAMLEnabled_) | 422 if (this.isSAMLEnabled_) |
| 348 this.supportChannel_.send({name: 'resetAuth'}); | 423 this.supportChannel_.send({name: 'resetAuth'}); |
| 349 } else if (msg.method == 'setAuthenticatedUserEmail' && | 424 } else if (msg.method == 'setAuthenticatedUserEmail' && |
| 350 this.isParentMessage_(e)) { | 425 this.isParentMessage_(e)) { |
| 351 if (this.attemptToken_ == msg.attemptToken) { | 426 if (this.attemptToken_ == msg.attemptToken) { |
| 352 this.email_ = msg.email; | 427 this.email_ = msg.email; |
| 353 this.maybeCompleteSAMLLogin_(); | 428 this.maybeCompleteSAMLLogin_(); |
| 354 } | 429 } |
| 355 } else if (msg.method == 'confirmLogin' && this.isInternalMessage_(e)) { | 430 } else if (msg.method == 'confirmLogin' && this.isInternalMessage_(e)) { |
| 356 if (this.attemptToken_ == msg.attemptToken) | 431 if (this.attemptToken_ == msg.attemptToken) |
| 357 this.onConfirmLogin_(); | 432 this.onConfirmLogin_(); |
| 358 else | 433 else |
| 359 console.error('Authenticator.onMessage: unexpected attemptToken!?'); | 434 console.error('Authenticator.onMessage: unexpected attemptToken!?'); |
| 360 } else if (msg.method == 'verifyConfirmedPassword' && | 435 } else if (msg.method == 'verifyConfirmedPassword' && |
| 361 this.isParentMessage_(e)) { | 436 this.isParentMessage_(e)) { |
| 362 this.onVerifyConfirmedPassword_(msg.password); | 437 this.onVerifyConfirmedPassword_(msg.password); |
| 363 } else if (msg.method == 'redirectToSignin' && | 438 } else if (msg.method == 'redirectToSignin' && |
| 364 this.isParentMessage_(e)) { | 439 this.isParentMessage_(e)) { |
| 365 $('gaia-frame').src = this.constructInitialFrameUrl_(); | 440 $('gaia-frame').src = this.constructInitialFrameUrl_(); |
| 366 } else { | 441 } else { |
| 367 console.error('Authenticator.onMessage: unknown message + origin!?'); | 442 console.error('Authenticator.onMessage: unknown message + origin!?'); |
| 368 } | 443 } |
| 369 } | 444 } |
| 370 }; | 445 }; |
| 371 | 446 |
| 372 Authenticator.getInstance().initialize(); | 447 Authenticator.getInstance().initialize(); |
| OLD | NEW |