OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 <include src="saml_handler.js"> | 5 <include src="saml_handler.js"> |
6 | 6 |
7 /** | 7 /** |
8 * @fileoverview An UI component to authenciate to Chrome. The component hosts | 8 * @fileoverview An UI component to authenciate to Chrome. The component hosts |
9 * IdP web pages in a webview. A client who is interested in monitoring | 9 * IdP web pages in a webview. A client who is interested in monitoring |
10 * authentication events should pass a listener object of type | 10 * authentication events should pass a listener object of type |
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 path = IDP_PATH; | 240 path = IDP_PATH; |
241 var url = this.idpOrigin_ + path; | 241 var url = this.idpOrigin_ + path; |
242 | 242 |
243 if (this.isNewGaiaFlowChromeOS) { | 243 if (this.isNewGaiaFlowChromeOS) { |
244 if (data.chromeType) | 244 if (data.chromeType) |
245 url = appendParam(url, 'chrometype', data.chromeType); | 245 url = appendParam(url, 'chrometype', data.chromeType); |
246 if (data.clientId) | 246 if (data.clientId) |
247 url = appendParam(url, 'client_id', data.clientId); | 247 url = appendParam(url, 'client_id', data.clientId); |
248 if (data.enterpriseDomain) | 248 if (data.enterpriseDomain) |
249 url = appendParam(url, 'manageddomain', data.enterpriseDomain); | 249 url = appendParam(url, 'manageddomain', data.enterpriseDomain); |
250 this.setDeviceId(data.deviceId); | 250 this.deviceId_ = data.deviceId; |
251 this.sessionIsEphemeral_ = data.sessionIsEphemeral; | 251 this.sessionIsEphemeral_ = data.sessionIsEphemeral; |
252 } else { | 252 } else { |
253 url = appendParam(url, 'continue', this.continueUrl_); | 253 url = appendParam(url, 'continue', this.continueUrl_); |
254 url = appendParam(url, 'service', data.service || SERVICE_ID); | 254 url = appendParam(url, 'service', data.service || SERVICE_ID); |
255 } | 255 } |
256 if (data.hl) | 256 if (data.hl) |
257 url = appendParam(url, 'hl', data.hl); | 257 url = appendParam(url, 'hl', data.hl); |
258 if (data.gaiaId) | 258 if (data.gaiaId) |
259 url = appendParam(url, 'user_id', data.gaiaId); | 259 url = appendParam(url, 'user_id', data.gaiaId); |
260 if (data.email) | 260 if (data.email) |
261 url = appendParam(url, 'Email', data.email); | 261 url = appendParam(url, 'Email', data.email); |
262 if (this.isConstrainedWindow_) | 262 if (this.isConstrainedWindow_) |
263 url = appendParam(url, 'source', CONSTRAINED_FLOW_SOURCE); | 263 url = appendParam(url, 'source', CONSTRAINED_FLOW_SOURCE); |
264 if (data.flow) | 264 if (data.flow) |
265 url = appendParam(url, 'flow', data.flow); | 265 url = appendParam(url, 'flow', data.flow); |
266 if (data.emailDomain) | 266 if (data.emailDomain) |
267 url = appendParam(url, 'emaildomain', data.emailDomain); | 267 url = appendParam(url, 'emaildomain', data.emailDomain); |
268 return url; | 268 return url; |
269 }; | 269 }; |
270 | 270 |
271 /** | 271 /** |
272 * Invoked when deviceId is set or updated. | |
273 */ | |
274 Authenticator.prototype.setDeviceId = function(deviceId) { | |
275 this.deviceId_ = deviceId; | |
276 }; | |
277 | |
278 /** | |
279 * Invoked when a main frame request in the webview has completed. | 272 * Invoked when a main frame request in the webview has completed. |
280 * @private | 273 * @private |
281 */ | 274 */ |
282 Authenticator.prototype.onRequestCompleted_ = function(details) { | 275 Authenticator.prototype.onRequestCompleted_ = function(details) { |
283 var currentUrl = details.url; | 276 var currentUrl = details.url; |
284 | 277 |
285 if (currentUrl.lastIndexOf(this.continueUrlWithoutParams_, 0) == 0) { | 278 if (currentUrl.lastIndexOf(this.continueUrlWithoutParams_, 0) == 0) { |
286 if (currentUrl.indexOf('ntp=1') >= 0) | 279 if (currentUrl.indexOf('ntp=1') >= 0) |
287 this.skipForNow_ = true; | 280 this.skipForNow_ = true; |
288 | 281 |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
393 * Handler for webView.request.onBeforeSendHeaders . | 386 * Handler for webView.request.onBeforeSendHeaders . |
394 * @return {!Object} Modified request headers. | 387 * @return {!Object} Modified request headers. |
395 * @private | 388 * @private |
396 */ | 389 */ |
397 Authenticator.prototype.onBeforeSendHeaders_ = function(details) { | 390 Authenticator.prototype.onBeforeSendHeaders_ = function(details) { |
398 // deviceId_ is empty when we do not need to send it. For example, | 391 // deviceId_ is empty when we do not need to send it. For example, |
399 // in case of device enrollment. | 392 // in case of device enrollment. |
400 if (this.isNewGaiaFlowChromeOS && this.deviceId_) { | 393 if (this.isNewGaiaFlowChromeOS && this.deviceId_) { |
401 var headers = details.requestHeaders; | 394 var headers = details.requestHeaders; |
402 var found = false; | 395 var found = false; |
403 var deviceId; | 396 var deviceId = this.getGAIADeviceId_(); |
404 if (this.sessionIsEphemeral_) | |
405 deviceId = EPHEMERAL_DEVICE_ID_PREFIX + this.deviceId_; | |
406 else | |
407 deviceId = this.deviceId_; | |
408 | 397 |
409 for (var i = 0, l = headers.length; i < l; ++i) { | 398 for (var i = 0, l = headers.length; i < l; ++i) { |
410 if (headers[i].name == X_DEVICE_ID_HEADER) { | 399 if (headers[i].name == X_DEVICE_ID_HEADER) { |
411 headers[i].value = deviceId; | 400 headers[i].value = deviceId; |
412 found = true; | 401 found = true; |
413 break; | 402 break; |
414 } | 403 } |
415 } | 404 } |
416 if (!found) { | 405 if (!found) { |
417 details.requestHeaders.push( | 406 details.requestHeaders.push( |
418 {name: X_DEVICE_ID_HEADER, value: deviceId}); | 407 {name: X_DEVICE_ID_HEADER, value: deviceId}); |
419 } | 408 } |
420 } | 409 } |
421 return { | 410 return { |
422 requestHeaders: details.requestHeaders | 411 requestHeaders: details.requestHeaders |
423 }; | 412 }; |
424 }; | 413 }; |
425 | 414 |
426 /** | 415 /** |
| 416 * Returns true if given HTML5 message is received from the webview element. |
| 417 * @param {object} e Payload of the received HTML5 message. |
| 418 */ |
| 419 Authenticator.prototype.isGaiaMessage = function(e) { |
| 420 if (!this.isWebviewEvent_(e)) |
| 421 return false; |
| 422 |
| 423 // The event origin does not have a trailing slash. |
| 424 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_.length - 1)) { |
| 425 return false; |
| 426 } |
| 427 |
| 428 // Gaia messages must be an object with 'method' property. |
| 429 if (typeof e.data != 'object' || !e.data.hasOwnProperty('method')) { |
| 430 return false; |
| 431 } |
| 432 return true; |
| 433 }; |
| 434 |
| 435 /** |
427 * Invoked when an HTML5 message is received from the webview element. | 436 * Invoked when an HTML5 message is received from the webview element. |
428 * @param {object} e Payload of the received HTML5 message. | 437 * @param {object} e Payload of the received HTML5 message. |
429 * @private | 438 * @private |
430 */ | 439 */ |
431 Authenticator.prototype.onMessageFromWebview_ = function(e) { | 440 Authenticator.prototype.onMessageFromWebview_ = function(e) { |
432 if (!this.isWebviewEvent_(e)) | 441 if (!this.isGaiaMessage(e)) |
433 return; | 442 return; |
434 | 443 |
435 // The event origin does not have a trailing slash. | |
436 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_.length - 1)) { | |
437 return; | |
438 } | |
439 | |
440 // Gaia messages must be an object with 'method' property. | |
441 if (typeof e.data != 'object' || !e.data.hasOwnProperty('method')) { | |
442 return; | |
443 } | |
444 | |
445 var msg = e.data; | 444 var msg = e.data; |
446 if (msg.method == 'attemptLogin') { | 445 if (msg.method == 'attemptLogin') { |
447 this.email_ = msg.email; | 446 this.email_ = msg.email; |
448 this.password_ = msg.password; | 447 this.password_ = msg.password; |
449 this.chooseWhatToSync_ = msg.chooseWhatToSync; | 448 this.chooseWhatToSync_ = msg.chooseWhatToSync; |
| 449 // We need to dispatch only first event, before user enters password. |
| 450 if (!msg.password) { |
| 451 this.dispatchEvent( |
| 452 new CustomEvent('attemptLogin', {detail: msg.email})); |
| 453 } |
450 } else if (msg.method == 'dialogShown') { | 454 } else if (msg.method == 'dialogShown') { |
451 this.dispatchEvent(new Event('dialogShown')); | 455 this.dispatchEvent(new Event('dialogShown')); |
452 } else if (msg.method == 'dialogHidden') { | 456 } else if (msg.method == 'dialogHidden') { |
453 this.dispatchEvent(new Event('dialogHidden')); | 457 this.dispatchEvent(new Event('dialogHidden')); |
454 } else if (msg.method == 'backButton') { | 458 } else if (msg.method == 'backButton') { |
455 this.dispatchEvent(new CustomEvent('backButton', {detail: msg.show})); | 459 this.dispatchEvent(new CustomEvent('backButton', {detail: msg.show})); |
456 } else if (msg.method == 'showView') { | 460 } else if (msg.method == 'showView') { |
457 this.dispatchEvent(new Event('showView')); | 461 this.dispatchEvent(new Event('showView')); |
458 } else { | 462 } else { |
459 console.warn('Unrecognized message from GAIA: ' + msg.method); | 463 console.warn('Unrecognized message from GAIA: ' + msg.method); |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
605 // webview from the zooming of other webviews using the 'per-view' zoom | 609 // webview from the zooming of other webviews using the 'per-view' zoom |
606 // mode, and then set it to 100% zoom. | 610 // mode, and then set it to 100% zoom. |
607 this.webview_.setZoomMode('per-view'); | 611 this.webview_.setZoomMode('per-view'); |
608 this.webview_.setZoom(1); | 612 this.webview_.setZoom(1); |
609 } | 613 } |
610 | 614 |
611 // Posts a message to IdP pages to initiate communication. | 615 // Posts a message to IdP pages to initiate communication. |
612 var currentUrl = this.webview_.src; | 616 var currentUrl = this.webview_.src; |
613 if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) { | 617 if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) { |
614 var msg = { | 618 var msg = { |
615 'method': 'handshake' | 619 'method': 'handshake', |
| 620 'deviceId': this.getGAIADeviceId_(), |
616 }; | 621 }; |
| 622 |
617 this.webview_.contentWindow.postMessage(msg, currentUrl); | 623 this.webview_.contentWindow.postMessage(msg, currentUrl); |
618 } | 624 } |
619 }; | 625 }; |
620 | 626 |
621 /** | 627 /** |
622 * Invoked when the webview fails loading a page. | 628 * Invoked when the webview fails loading a page. |
623 * @private | 629 * @private |
624 */ | 630 */ |
625 Authenticator.prototype.onLoadAbort_ = function(e) { | 631 Authenticator.prototype.onLoadAbort_ = function(e) { |
626 this.dispatchEvent(new CustomEvent('loadAbort', | 632 this.dispatchEvent(new CustomEvent('loadAbort', |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
658 */ | 664 */ |
659 Authenticator.prototype.isWebviewEvent_ = function(e) { | 665 Authenticator.prototype.isWebviewEvent_ = function(e) { |
660 // Note: <webview> prints error message to console if |contentWindow| is not | 666 // Note: <webview> prints error message to console if |contentWindow| is not |
661 // defined. | 667 // defined. |
662 // TODO(dzhioev): remove the message. http://crbug.com/469522 | 668 // TODO(dzhioev): remove the message. http://crbug.com/469522 |
663 var webviewWindow = this.webview_.contentWindow; | 669 var webviewWindow = this.webview_.contentWindow; |
664 return !!webviewWindow && webviewWindow === e.source; | 670 return !!webviewWindow && webviewWindow === e.source; |
665 }; | 671 }; |
666 | 672 |
667 /** | 673 /** |
| 674 * Format deviceId for GAIA . |
| 675 * @return {string} deviceId. |
| 676 * @private |
| 677 */ |
| 678 Authenticator.prototype.getGAIADeviceId_ = function() { |
| 679 // deviceId_ is empty when we do not need to send it. For example, |
| 680 // in case of device enrollment. |
| 681 if (!(this.isNewGaiaFlowChromeOS && this.deviceId_)) |
| 682 return; |
| 683 |
| 684 if (this.sessionIsEphemeral_) |
| 685 return EPHEMERAL_DEVICE_ID_PREFIX + this.deviceId_; |
| 686 else |
| 687 return this.deviceId_; |
| 688 }; |
| 689 |
| 690 /** |
| 691 * Informs Gaia of new deviceId to be used. |
| 692 */ |
| 693 Authenticator.prototype.updateDeviceId = function(deviceId) { |
| 694 this.deviceId_ = deviceId; |
| 695 var msg = { |
| 696 'method': 'updateDeviceId', |
| 697 'deviceId': this.getGAIADeviceId_(), |
| 698 }; |
| 699 |
| 700 var currentUrl = this.webview_.src; |
| 701 this.webview_.contentWindow.postMessage(msg, currentUrl); |
| 702 }; |
| 703 |
| 704 /** |
668 * The current auth flow of the hosted auth page. | 705 * The current auth flow of the hosted auth page. |
669 * @type {AuthFlow} | 706 * @type {AuthFlow} |
670 */ | 707 */ |
671 cr.defineProperty(Authenticator, 'authFlow'); | 708 cr.defineProperty(Authenticator, 'authFlow'); |
672 | 709 |
673 Authenticator.AuthFlow = AuthFlow; | 710 Authenticator.AuthFlow = AuthFlow; |
674 Authenticator.AuthMode = AuthMode; | 711 Authenticator.AuthMode = AuthMode; |
675 Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS; | 712 Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS; |
676 | 713 |
677 return { | 714 return { |
678 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old | 715 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old |
679 // iframe-based flow is deprecated. | 716 // iframe-based flow is deprecated. |
680 GaiaAuthHost: Authenticator, | 717 GaiaAuthHost: Authenticator, |
681 Authenticator: Authenticator | 718 Authenticator: Authenticator |
682 }; | 719 }; |
683 }); | 720 }); |
OLD | NEW |