| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 * @fileoverview An UI component to host gaia auth extension in an iframe. | 6 * @fileoverview An UI component to host gaia auth extension in an iframe. |
| 7 * After the component binds with an iframe, call its {@code load} to start the | 7 * After the component binds with an iframe, call its {@code load} to start the |
| 8 * authentication flow. There are two events would be raised after this point: | 8 * authentication flow. There are two events would be raised after this point: |
| 9 * a 'ready' event when the authentication UI is ready to use and a 'completed' | 9 * a 'ready' event when the authentication UI is ready to use and a 'completed' |
| 10 * event when the authentication is completed successfully. If caller is | 10 * event when the authentication is completed successfully. If caller is |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 */ | 28 */ |
| 29 var AUTH_URL = AUTH_URL_BASE + '/main.html'; | 29 var AUTH_URL = AUTH_URL_BASE + '/main.html'; |
| 30 | 30 |
| 31 /** | 31 /** |
| 32 * Auth URL to use for offline flow. | 32 * Auth URL to use for offline flow. |
| 33 * @const | 33 * @const |
| 34 */ | 34 */ |
| 35 var OFFLINE_AUTH_URL = AUTH_URL_BASE + '/offline.html'; | 35 var OFFLINE_AUTH_URL = AUTH_URL_BASE + '/offline.html'; |
| 36 | 36 |
| 37 /** | 37 /** |
| 38 * Auth URL to use for inline flow. | |
| 39 * @const | |
| 40 */ | |
| 41 var INLINE_AUTH_URL = AUTH_URL_BASE + '/inline_main.html'; | |
| 42 | |
| 43 /** | |
| 44 * Origin of the gaia sign in page. | 38 * Origin of the gaia sign in page. |
| 45 * @const | 39 * @const |
| 46 */ | 40 */ |
| 47 var GAIA_ORIGIN = 'https://accounts.google.com'; | 41 var GAIA_ORIGIN = 'https://accounts.google.com'; |
| 48 | 42 |
| 49 /** | 43 /** |
| 50 * Supported params of auth extension. For a complete list, check out the | 44 * Supported params of auth extension. For a complete list, check out the |
| 51 * auth extension's main.js. | 45 * auth extension's main.js. |
| 52 * @type {!Array.<string>} | 46 * @type {!Array.<string>} |
| 53 * @const | 47 * @const |
| 54 */ | 48 */ |
| 55 var SUPPORTED_PARAMS = [ | 49 var SUPPORTED_PARAMS = [ |
| 56 'gaiaUrl', // Gaia url to use; | 50 'gaiaUrl', // Gaia url to use; |
| 57 'gaiaPath', // Gaia path to use without a leading slash; | 51 'gaiaPath', // Gaia path to use without a leading slash; |
| 58 'hl', // Language code for the user interface; | 52 'hl', // Language code for the user interface; |
| 59 'email', // Pre-fill the email field in Gaia UI; | 53 'email', // Pre-fill the email field in Gaia UI; |
| 60 'service', // Name of Gaia service; | 54 'service', // Name of Gaia service; |
| 61 'continueUrl', // Continue url to use; | 55 'continueUrl', // Continue url to use; |
| 62 'partitionId', // Partition ID for the embedded Gaia webview; | |
| 63 'frameUrl', // Initial frame URL to use. If empty defaults to gaiaUrl. | 56 'frameUrl', // Initial frame URL to use. If empty defaults to gaiaUrl. |
| 64 'constrained' // Whether the extension is loaded in a constrained window; | 57 'constrained' // Whether the extension is loaded in a constrained window; |
| 65 ]; | 58 ]; |
| 66 | 59 |
| 67 /** | 60 /** |
| 68 * Supported localized strings. For a complete list, check out the auth | 61 * Supported localized strings. For a complete list, check out the auth |
| 69 * extension's offline.js | 62 * extension's offline.js |
| 70 * @type {!Array.<string>} | 63 * @type {!Array.<string>} |
| 71 * @const | 64 * @const |
| 72 */ | 65 */ |
| 73 var LOCALIZED_STRING_PARAMS = [ | 66 var LOCALIZED_STRING_PARAMS = [ |
| 74 'stringSignIn', | 67 'stringSignIn', |
| 75 'stringEmail', | 68 'stringEmail', |
| 76 'stringPassword', | 69 'stringPassword', |
| 77 'stringEmptyEmail', | 70 'stringEmptyEmail', |
| 78 'stringEmptyPassword', | 71 'stringEmptyPassword', |
| 79 'stringError' | 72 'stringError' |
| 80 ]; | 73 ]; |
| 81 | 74 |
| 82 /** | 75 /** |
| 83 * Enum for the authorization mode, must match AuthMode defined in | 76 * Enum for the authorization mode, must match AuthMode defined in |
| 84 * chrome/browser/ui/webui/inline_login_ui.cc. | 77 * chrome/browser/ui/webui/inline_login_ui.cc. |
| 85 * @enum {number} | 78 * @enum {number} |
| 86 */ | 79 */ |
| 87 var AuthMode = { | 80 var AuthMode = { |
| 88 DEFAULT: 0, | 81 DEFAULT: 0, |
| 89 OFFLINE: 1, | 82 OFFLINE: 1, |
| 90 INLINE: 2 | 83 DESKTOP: 2 |
| 91 }; | 84 }; |
| 92 | 85 |
| 93 /** | 86 /** |
| 94 * Enum for the auth flow. | 87 * Enum for the auth flow. |
| 95 * @enum {number} | 88 * @enum {number} |
| 96 */ | 89 */ |
| 97 var AuthFlow = { | 90 var AuthFlow = { |
| 98 GAIA: 0, | 91 GAIA: 0, |
| 99 SAML: 1 | 92 SAML: 1 |
| 100 }; | 93 }; |
| 101 | 94 |
| 102 /** | 95 /** |
| 103 * Creates a new gaia auth extension host. | 96 * Creates a new gaia auth extension host. |
| 104 * @param {HTMLIFrameElement|string} container The iframe element or its id | 97 * @param {HTMLIFrameElement|string} container The iframe element or its id |
| 105 * to host the auth extension. | 98 * to host the auth extension. |
| 106 * @constructor | 99 * @constructor |
| 107 * @extends {cr.EventTarget} | 100 * @extends {cr.EventTarget} |
| 108 */ | 101 */ |
| 109 function GaiaAuthHost(container) { | 102 function GaiaAuthHost(container) { |
| 110 this.frame_ = typeof container == 'string' ? $(container) : container; | 103 this.frame_ = typeof container == 'string' ? $(container) : container; |
| 111 assert(this.frame_); | 104 assert(this.frame_); |
| 112 window.addEventListener('message', | 105 window.addEventListener('message', |
| 113 this.onMessage_.bind(this), false); | 106 this.onMessage_.bind(this), false); |
| 114 window.addEventListener('popstate', | |
| 115 this.onPopState_.bind(this), false); | |
| 116 } | 107 } |
| 117 | 108 |
| 118 GaiaAuthHost.prototype = { | 109 GaiaAuthHost.prototype = { |
| 119 __proto__: cr.EventTarget.prototype, | 110 __proto__: cr.EventTarget.prototype, |
| 120 | 111 |
| 121 /** | 112 /** |
| 122 * An url to use with {@code reload}. | 113 * An url to use with {@code reload}. |
| 123 * @type {?string} | 114 * @type {?string} |
| 124 * @private | 115 * @private |
| 125 */ | 116 */ |
| 126 reloadUrl_: null, | 117 reloadUrl_: null, |
| 127 | 118 |
| 128 /** | 119 /** |
| 129 * The domain name of the current auth page. | 120 * The domain name of the current auth page. |
| 130 * @type {string} | 121 * @type {string} |
| 131 */ | 122 */ |
| 132 authDomain: '', | 123 authDomain: '', |
| 133 | 124 |
| 134 /** | 125 /** |
| 135 * Invoked when authentication is completed successfully with credential | 126 * Invoked when authentication is completed successfully with credential |
| 136 * data. A credential data object looks like this: | 127 * data. A credential data object looks like this: |
| 137 * <pre> | 128 * <pre> |
| 138 * {@code | 129 * {@code |
| 139 * { | 130 * { |
| 140 * email: 'xx@gmail.com', | 131 * email: 'xx@gmail.com', |
| 141 * password: 'xxxx', // May not present | 132 * password: 'xxxx', // May not present |
| 142 * authCode: 'x/xx', // May not present | 133 * authCode: 'x/xx', // May not present |
| 143 * authMode: 'x', // Authorization mode, default/inline/offline. | 134 * authMode: 'x', // Authorization mode, default/offline/desktop. |
| 144 * } | 135 * } |
| 145 * } | 136 * } |
| 146 * </pre> | 137 * </pre> |
| 147 * @type {function(Object)} | 138 * @type {function(Object)} |
| 148 * @private | 139 * @private |
| 149 */ | 140 */ |
| 150 successCallback_: null, | 141 successCallback_: null, |
| 151 | 142 |
| 152 /** | 143 /** |
| 153 * Invoked when GAIA indicates login success and SAML was used. At this | 144 * Invoked when GAIA indicates login success and SAML was used. At this |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 | 229 |
| 239 populateParams(SUPPORTED_PARAMS, data); | 230 populateParams(SUPPORTED_PARAMS, data); |
| 240 populateParams(LOCALIZED_STRING_PARAMS, data.localizedStrings); | 231 populateParams(LOCALIZED_STRING_PARAMS, data.localizedStrings); |
| 241 params.push('parentPage=' + encodeURIComponent(window.location.origin)); | 232 params.push('parentPage=' + encodeURIComponent(window.location.origin)); |
| 242 | 233 |
| 243 var url; | 234 var url; |
| 244 switch (authMode) { | 235 switch (authMode) { |
| 245 case AuthMode.OFFLINE: | 236 case AuthMode.OFFLINE: |
| 246 url = OFFLINE_AUTH_URL; | 237 url = OFFLINE_AUTH_URL; |
| 247 break; | 238 break; |
| 248 case AuthMode.INLINE: | 239 case AuthMode.DESKTOP: |
| 249 url = INLINE_AUTH_URL; | 240 url = AUTH_URL; |
| 250 params.push('inlineMode=1'); | 241 params.push('desktopMode=1'); |
| 251 break; | 242 break; |
| 252 default: | 243 default: |
| 253 url = AUTH_URL; | 244 url = AUTH_URL; |
| 254 } | 245 } |
| 255 url += '?' + params.join('&'); | 246 url += '?' + params.join('&'); |
| 256 | 247 |
| 257 this.frame_.src = url; | 248 this.frame_.src = url; |
| 258 this.reloadUrl_ = url; | 249 this.reloadUrl_ = url; |
| 259 this.successCallback_ = successCallback; | 250 this.successCallback_ = successCallback; |
| 260 this.authFlow = AuthFlow.GAIA; | 251 this.authFlow = AuthFlow.GAIA; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 e.source == this.frame_.contentWindow; | 310 e.source == this.frame_.contentWindow; |
| 320 }, | 311 }, |
| 321 | 312 |
| 322 /** | 313 /** |
| 323 * Event handler that is invoked when HTML5 message is received. | 314 * Event handler that is invoked when HTML5 message is received. |
| 324 * @param {object} e Payload of the received HTML5 message. | 315 * @param {object} e Payload of the received HTML5 message. |
| 325 */ | 316 */ |
| 326 onMessage_: function(e) { | 317 onMessage_: function(e) { |
| 327 var msg = e.data; | 318 var msg = e.data; |
| 328 | 319 |
| 329 // In the inline sign in flow, the embedded gaia webview posts credential | |
| 330 // directly to the inline sign in page, because its parent JavaScript | |
| 331 // reference points to the top frame of the embedder instead of the sub | |
| 332 // frame of the gaia auth extension. | |
| 333 if (e.origin == GAIA_ORIGIN && msg.method == 'attemptLogin') { | |
| 334 this.email_ = msg.email; | |
| 335 this.password_ = msg.password; | |
| 336 this.chooseWhatToSync_ = msg.chooseWhatToSync; | |
| 337 return; | |
| 338 } | |
| 339 | |
| 340 if (!this.isAuthExtMessage_(e)) | 320 if (!this.isAuthExtMessage_(e)) |
| 341 return; | 321 return; |
| 342 | 322 |
| 343 if (msg.method == 'loginUILoaded') { | 323 if (msg.method == 'loginUILoaded') { |
| 344 cr.dispatchSimpleEvent(this, 'ready'); | 324 cr.dispatchSimpleEvent(this, 'ready'); |
| 345 return; | 325 return; |
| 346 } | 326 } |
| 347 | 327 |
| 348 if (/^complete(Login|Authentication)$|^offlineLogin$/.test(msg.method)) { | 328 if (/^complete(Login|Authentication)$|^offlineLogin$/.test(msg.method)) { |
| 349 if (!msg.email && !this.email_ && !msg.skipForNow) { | 329 if (!msg.email && !this.email_ && !msg.skipForNow) { |
| 350 var msg = {method: 'redirectToSignin'}; | 330 var msg = {method: 'redirectToSignin'}; |
| 351 this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE); | 331 this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE); |
| 352 return; | 332 return; |
| 353 } | 333 } |
| 354 this.onAuthSuccess_({email: msg.email || this.email_, | 334 this.onAuthSuccess_({email: msg.email, |
| 355 password: msg.password || this.password_, | 335 password: msg.password, |
| 356 authCode: msg.authCode, | |
| 357 useOffline: msg.method == 'offlineLogin', | 336 useOffline: msg.method == 'offlineLogin', |
| 358 usingSAML: msg.usingSAML || false, | 337 usingSAML: msg.usingSAML || false, |
| 359 chooseWhatToSync: this.chooseWhatToSync_, | 338 chooseWhatToSync: msg.chooseWhatToSync, |
| 360 skipForNow: msg.skipForNow || false }); | 339 skipForNow: msg.skipForNow || false, |
| 340 sessionIndex: msg.sessionIndex || ''}); |
| 361 return; | 341 return; |
| 362 } | 342 } |
| 363 | 343 |
| 364 if (msg.method == 'retrieveAuthenticatedUserEmail') { | 344 if (msg.method == 'retrieveAuthenticatedUserEmail') { |
| 365 if (this.retrieveAuthenticatedUserEmailCallback_) { | 345 if (this.retrieveAuthenticatedUserEmailCallback_) { |
| 366 this.retrieveAuthenticatedUserEmailCallback_(msg.attemptToken); | 346 this.retrieveAuthenticatedUserEmailCallback_(msg.attemptToken); |
| 367 } else { | 347 } else { |
| 368 console.error( | 348 console.error( |
| 369 'GaiaAuthHost: Invalid retrieveAuthenticatedUserEmailCallback_.'); | 349 'GaiaAuthHost: Invalid retrieveAuthenticatedUserEmailCallback_.'); |
| 370 } | 350 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 386 console.error('GaiaAuthHost: Invalid noPasswordCallback_.'); | 366 console.error('GaiaAuthHost: Invalid noPasswordCallback_.'); |
| 387 return; | 367 return; |
| 388 } | 368 } |
| 389 | 369 |
| 390 if (msg.method == 'authPageLoaded') { | 370 if (msg.method == 'authPageLoaded') { |
| 391 this.authDomain = msg.domain; | 371 this.authDomain = msg.domain; |
| 392 this.authFlow = msg.isSAML ? AuthFlow.SAML : AuthFlow.GAIA; | 372 this.authFlow = msg.isSAML ? AuthFlow.SAML : AuthFlow.GAIA; |
| 393 return; | 373 return; |
| 394 } | 374 } |
| 395 | 375 |
| 396 if (msg.method == 'reportState') { | |
| 397 var newUrl = setQueryParam(location, 'frameUrl', msg.src); | |
| 398 if (history.state) { | |
| 399 if (history.state.src != msg.src) { | |
| 400 history.pushState({src: msg.src}, '', newUrl); | |
| 401 } | |
| 402 } else { | |
| 403 history.replaceState({src: msg.src}); | |
| 404 } | |
| 405 return; | |
| 406 } | |
| 407 | |
| 408 if (msg.method == 'switchToFullTab') { | 376 if (msg.method == 'switchToFullTab') { |
| 409 chrome.send('switchToFullTab', [msg.url]); | 377 chrome.send('switchToFullTab', [msg.url]); |
| 410 return; | 378 return; |
| 411 } | 379 } |
| 412 | 380 |
| 413 console.error('Unknown message method=' + msg.method); | 381 console.error('Unknown message method=' + msg.method); |
| 414 }, | |
| 415 | |
| 416 /** | |
| 417 * Event handler that is invoked when the history state is changed. | |
| 418 * @param {object} e The popstate event being triggered. | |
| 419 */ | |
| 420 onPopState_: function(e) { | |
| 421 var state = e.state; | |
| 422 if (state) { | |
| 423 var msg = { | |
| 424 method: 'navigate', | |
| 425 src: state.src | |
| 426 }; | |
| 427 this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE); | |
| 428 } | |
| 429 } | 382 } |
| 430 }; | 383 }; |
| 431 | 384 |
| 432 /** | 385 /** |
| 433 * The current auth flow of the hosted gaia_auth extension. | 386 * The current auth flow of the hosted gaia_auth extension. |
| 434 * @type {AuthFlow} | 387 * @type {AuthFlow} |
| 435 */ | 388 */ |
| 436 cr.defineProperty(GaiaAuthHost, 'authFlow'); | 389 cr.defineProperty(GaiaAuthHost, 'authFlow'); |
| 437 | 390 |
| 438 GaiaAuthHost.SUPPORTED_PARAMS = SUPPORTED_PARAMS; | 391 GaiaAuthHost.SUPPORTED_PARAMS = SUPPORTED_PARAMS; |
| 439 GaiaAuthHost.LOCALIZED_STRING_PARAMS = LOCALIZED_STRING_PARAMS; | 392 GaiaAuthHost.LOCALIZED_STRING_PARAMS = LOCALIZED_STRING_PARAMS; |
| 440 GaiaAuthHost.AuthMode = AuthMode; | 393 GaiaAuthHost.AuthMode = AuthMode; |
| 441 GaiaAuthHost.AuthFlow = AuthFlow; | 394 GaiaAuthHost.AuthFlow = AuthFlow; |
| 442 | 395 |
| 443 return { | 396 return { |
| 444 GaiaAuthHost: GaiaAuthHost | 397 GaiaAuthHost: GaiaAuthHost |
| 445 }; | 398 }; |
| 446 }); | 399 }); |
| OLD | NEW |