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 msg.apiUsed); | 347 msg.apiUsed); |
368 } else { | 348 } else { |
369 console.error( | 349 console.error( |
370 'GaiaAuthHost: Invalid retrieveAuthenticatedUserEmailCallback_.'); | 350 'GaiaAuthHost: Invalid retrieveAuthenticatedUserEmailCallback_.'); |
(...skipping 16 matching lines...) Expand all Loading... |
387 console.error('GaiaAuthHost: Invalid noPasswordCallback_.'); | 367 console.error('GaiaAuthHost: Invalid noPasswordCallback_.'); |
388 return; | 368 return; |
389 } | 369 } |
390 | 370 |
391 if (msg.method == 'authPageLoaded') { | 371 if (msg.method == 'authPageLoaded') { |
392 this.authDomain = msg.domain; | 372 this.authDomain = msg.domain; |
393 this.authFlow = msg.isSAML ? AuthFlow.SAML : AuthFlow.GAIA; | 373 this.authFlow = msg.isSAML ? AuthFlow.SAML : AuthFlow.GAIA; |
394 return; | 374 return; |
395 } | 375 } |
396 | 376 |
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 | |
409 if (msg.method == 'switchToFullTab') { | 377 if (msg.method == 'switchToFullTab') { |
410 chrome.send('switchToFullTab', [msg.url]); | 378 chrome.send('switchToFullTab', [msg.url]); |
411 return; | 379 return; |
412 } | 380 } |
413 | 381 |
414 console.error('Unknown message method=' + msg.method); | 382 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 } | |
430 } | 383 } |
431 }; | 384 }; |
432 | 385 |
433 /** | 386 /** |
434 * The current auth flow of the hosted gaia_auth extension. | 387 * The current auth flow of the hosted gaia_auth extension. |
435 * @type {AuthFlow} | 388 * @type {AuthFlow} |
436 */ | 389 */ |
437 cr.defineProperty(GaiaAuthHost, 'authFlow'); | 390 cr.defineProperty(GaiaAuthHost, 'authFlow'); |
438 | 391 |
439 GaiaAuthHost.SUPPORTED_PARAMS = SUPPORTED_PARAMS; | 392 GaiaAuthHost.SUPPORTED_PARAMS = SUPPORTED_PARAMS; |
440 GaiaAuthHost.LOCALIZED_STRING_PARAMS = LOCALIZED_STRING_PARAMS; | 393 GaiaAuthHost.LOCALIZED_STRING_PARAMS = LOCALIZED_STRING_PARAMS; |
441 GaiaAuthHost.AuthMode = AuthMode; | 394 GaiaAuthHost.AuthMode = AuthMode; |
442 GaiaAuthHost.AuthFlow = AuthFlow; | 395 GaiaAuthHost.AuthFlow = AuthFlow; |
443 | 396 |
444 return { | 397 return { |
445 GaiaAuthHost: GaiaAuthHost | 398 GaiaAuthHost: GaiaAuthHost |
446 }; | 399 }; |
447 }); | 400 }); |
OLD | NEW |