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