| 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 11 matching lines...) Expand all Loading... |
| 22 var IDP_PATH = 'ServiceLogin?skipvpage=true&sarp=1&rm=hide'; | 22 var IDP_PATH = 'ServiceLogin?skipvpage=true&sarp=1&rm=hide'; |
| 23 var CONTINUE_URL = | 23 var CONTINUE_URL = |
| 24 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/success.html'; | 24 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/success.html'; |
| 25 var SIGN_IN_HEADER = 'google-accounts-signin'; | 25 var SIGN_IN_HEADER = 'google-accounts-signin'; |
| 26 var EMBEDDED_FORM_HEADER = 'google-accounts-embedded'; | 26 var EMBEDDED_FORM_HEADER = 'google-accounts-embedded'; |
| 27 var LOCATION_HEADER = 'location'; | 27 var LOCATION_HEADER = 'location'; |
| 28 var SET_COOKIE_HEADER = 'set-cookie'; | 28 var SET_COOKIE_HEADER = 'set-cookie'; |
| 29 var OAUTH_CODE_COOKIE = 'oauth_code'; | 29 var OAUTH_CODE_COOKIE = 'oauth_code'; |
| 30 var SERVICE_ID = 'chromeoslogin'; | 30 var SERVICE_ID = 'chromeoslogin'; |
| 31 var EMBEDDED_SETUP_CHROMEOS_ENDPOINT = 'embedded/setup/chromeos'; | 31 var EMBEDDED_SETUP_CHROMEOS_ENDPOINT = 'embedded/setup/chromeos'; |
| 32 var X_DEVICE_ID_HEADER = 'X-Device-ID'; | |
| 33 var EPHEMERAL_DEVICE_ID_PREFIX = 't_'; | |
| 34 | 32 |
| 35 /** | 33 /** |
| 36 * The source URL parameter for the constrained signin flow. | 34 * The source URL parameter for the constrained signin flow. |
| 37 */ | 35 */ |
| 38 var CONSTRAINED_FLOW_SOURCE = 'chrome'; | 36 var CONSTRAINED_FLOW_SOURCE = 'chrome'; |
| 39 | 37 |
| 40 /** | 38 /** |
| 41 * Enum for the authorization mode, must match AuthMode defined in | 39 * Enum for the authorization mode, must match AuthMode defined in |
| 42 * chrome/browser/ui/webui/inline_login_ui.cc. | 40 * chrome/browser/ui/webui/inline_login_ui.cc. |
| 43 * @enum {number} | 41 * @enum {number} |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 78 'clientId', // Chrome client id. | 76 'clientId', // Chrome client id. |
| 79 'useEafe', // Whether to use EAFE. | 77 'useEafe', // Whether to use EAFE. |
| 80 'needPassword', // Whether the host is interested in getting a password. | 78 'needPassword', // Whether the host is interested in getting a password. |
| 81 // If this set to |false|, |confirmPasswordCallback| is | 79 // If this set to |false|, |confirmPasswordCallback| is |
| 82 // not called before dispatching |authCopleted|. | 80 // not called before dispatching |authCopleted|. |
| 83 // Default is |true|. | 81 // Default is |true|. |
| 84 'flow', // One of 'default', 'enterprise', or 'theftprotection'. | 82 'flow', // One of 'default', 'enterprise', or 'theftprotection'. |
| 85 'enterpriseDomain', // Domain in which hosting device is (or should be) | 83 'enterpriseDomain', // Domain in which hosting device is (or should be) |
| 86 // enrolled. | 84 // enrolled. |
| 87 'emailDomain', // Value used to prefill domain for email. | 85 'emailDomain', // Value used to prefill domain for email. |
| 88 'deviceId', // User device ID (sync Id). | |
| 89 'sessionIsEphemeral', // User session would be ephemeral. | |
| 90 'clientVersion', // Version of the Chrome build. | 86 'clientVersion', // Version of the Chrome build. |
| 91 'platformVersion', // Version of the OS build. | 87 'platformVersion', // Version of the OS build. |
| 92 'releaseChannel', // Installation channel. | 88 'releaseChannel', // Installation channel. |
| 93 'endpointGen', // Current endpoint generation. | 89 'endpointGen', // Current endpoint generation. |
| 94 ]; | 90 ]; |
| 95 | 91 |
| 96 /** | 92 /** |
| 97 * Initializes the authenticator component. | 93 * Initializes the authenticator component. |
| 98 * @param {webview|string} webview The webview element or its ID to host IdP | 94 * @param {webview|string} webview The webview element or its ID to host IdP |
| 99 * web pages. | 95 * web pages. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 112 this.authFlow = AuthFlow.DEFAULT; | 108 this.authFlow = AuthFlow.DEFAULT; |
| 113 this.authDomain = ''; | 109 this.authDomain = ''; |
| 114 this.loaded_ = false; | 110 this.loaded_ = false; |
| 115 this.idpOrigin_ = null; | 111 this.idpOrigin_ = null; |
| 116 this.continueUrl_ = null; | 112 this.continueUrl_ = null; |
| 117 this.continueUrlWithoutParams_ = null; | 113 this.continueUrlWithoutParams_ = null; |
| 118 this.initialFrameUrl_ = null; | 114 this.initialFrameUrl_ = null; |
| 119 this.reloadUrl_ = null; | 115 this.reloadUrl_ = null; |
| 120 this.trusted_ = true; | 116 this.trusted_ = true; |
| 121 this.oauth_code_ = null; | 117 this.oauth_code_ = null; |
| 122 this.deviceId_ = null; | |
| 123 this.sessionIsEphemeral_ = null; | |
| 124 this.onBeforeSetHeadersSet_ = false; | |
| 125 | 118 |
| 126 this.useEafe_ = false; | 119 this.useEafe_ = false; |
| 127 this.clientId_ = null; | 120 this.clientId_ = null; |
| 128 | 121 |
| 129 this.samlHandler_ = new cr.login.SamlHandler(this.webview_); | 122 this.samlHandler_ = new cr.login.SamlHandler(this.webview_); |
| 130 this.confirmPasswordCallback = null; | 123 this.confirmPasswordCallback = null; |
| 131 this.noPasswordCallback = null; | 124 this.noPasswordCallback = null; |
| 132 this.insecureContentBlockedCallback = null; | 125 this.insecureContentBlockedCallback = null; |
| 133 this.samlApiUsedCallback = null; | 126 this.samlApiUsedCallback = null; |
| 134 this.missingGaiaInfoCallback = null; | 127 this.missingGaiaInfoCallback = null; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 // Don't block insecure content for desktop flow because it lands on | 202 // Don't block insecure content for desktop flow because it lands on |
| 210 // http. Otherwise, block insecure content as long as gaia is https. | 203 // http. Otherwise, block insecure content as long as gaia is https. |
| 211 this.samlHandler_.blockInsecureContent = authMode != AuthMode.DESKTOP && | 204 this.samlHandler_.blockInsecureContent = authMode != AuthMode.DESKTOP && |
| 212 this.idpOrigin_.indexOf('https://') == 0; | 205 this.idpOrigin_.indexOf('https://') == 0; |
| 213 this.needPassword = !('needPassword' in data) || data.needPassword; | 206 this.needPassword = !('needPassword' in data) || data.needPassword; |
| 214 | 207 |
| 215 if (this.isNewGaiaFlowChromeOS) { | 208 if (this.isNewGaiaFlowChromeOS) { |
| 216 this.webview_.contextMenus.onShow.addListener(function(e) { | 209 this.webview_.contextMenus.onShow.addListener(function(e) { |
| 217 e.preventDefault(); | 210 e.preventDefault(); |
| 218 }); | 211 }); |
| 219 if (!this.onBeforeSetHeadersSet_) { | |
| 220 this.onBeforeSetHeadersSet_ = true; | |
| 221 var filterPrefix = this.idpOrigin_ + EMBEDDED_SETUP_CHROMEOS_ENDPOINT; | |
| 222 this.webview_.request.onBeforeSendHeaders.addListener( | |
| 223 this.onBeforeSendHeaders_.bind(this), | |
| 224 {urls: [filterPrefix + '?*', filterPrefix + '/*']}, | |
| 225 ['requestHeaders', 'blocking']); | |
| 226 } | |
| 227 } | 212 } |
| 228 | 213 |
| 229 this.webview_.src = this.reloadUrl_; | 214 this.webview_.src = this.reloadUrl_; |
| 230 }; | 215 }; |
| 231 | 216 |
| 232 /** | 217 /** |
| 233 * Reloads the authenticator component. | 218 * Reloads the authenticator component. |
| 234 */ | 219 */ |
| 235 Authenticator.prototype.reload = function() { | 220 Authenticator.prototype.reload = function() { |
| 236 this.clearCredentials_(); | 221 this.clearCredentials_(); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 254 if (data.enterpriseDomain) | 239 if (data.enterpriseDomain) |
| 255 url = appendParam(url, 'manageddomain', data.enterpriseDomain); | 240 url = appendParam(url, 'manageddomain', data.enterpriseDomain); |
| 256 if (data.clientVersion) | 241 if (data.clientVersion) |
| 257 url = appendParam(url, 'client_version', data.clientVersion); | 242 url = appendParam(url, 'client_version', data.clientVersion); |
| 258 if (data.platformVersion) | 243 if (data.platformVersion) |
| 259 url = appendParam(url, 'platform_version', data.platformVersion); | 244 url = appendParam(url, 'platform_version', data.platformVersion); |
| 260 if (data.releaseChannel) | 245 if (data.releaseChannel) |
| 261 url = appendParam(url, 'release_channel', data.releaseChannel); | 246 url = appendParam(url, 'release_channel', data.releaseChannel); |
| 262 if (data.endpointGen) | 247 if (data.endpointGen) |
| 263 url = appendParam(url, 'endpoint_gen', data.endpointGen); | 248 url = appendParam(url, 'endpoint_gen', data.endpointGen); |
| 264 this.deviceId_ = data.deviceId; | |
| 265 this.sessionIsEphemeral_ = data.sessionIsEphemeral; | |
| 266 } else { | 249 } else { |
| 267 url = appendParam(url, 'continue', this.continueUrl_); | 250 url = appendParam(url, 'continue', this.continueUrl_); |
| 268 url = appendParam(url, 'service', data.service || SERVICE_ID); | 251 url = appendParam(url, 'service', data.service || SERVICE_ID); |
| 269 } | 252 } |
| 270 if (data.hl) | 253 if (data.hl) |
| 271 url = appendParam(url, 'hl', data.hl); | 254 url = appendParam(url, 'hl', data.hl); |
| 272 if (data.gaiaId) | 255 if (data.gaiaId) |
| 273 url = appendParam(url, 'user_id', data.gaiaId); | 256 url = appendParam(url, 'user_id', data.gaiaId); |
| 274 if (data.email) | 257 if (data.email) |
| 275 url = appendParam(url, 'Email', data.email); | 258 url = appendParam(url, 'Email', data.email); |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 var headerValue = header.value; | 373 var headerValue = header.value; |
| 391 if (headerValue.indexOf(OAUTH_CODE_COOKIE + '=', 0) == 0) { | 374 if (headerValue.indexOf(OAUTH_CODE_COOKIE + '=', 0) == 0) { |
| 392 this.oauth_code_ = | 375 this.oauth_code_ = |
| 393 headerValue.substring(OAUTH_CODE_COOKIE.length + 1).split(';')[0]; | 376 headerValue.substring(OAUTH_CODE_COOKIE.length + 1).split(';')[0]; |
| 394 } | 377 } |
| 395 } | 378 } |
| 396 } | 379 } |
| 397 }; | 380 }; |
| 398 | 381 |
| 399 /** | 382 /** |
| 400 * Handler for webView.request.onBeforeSendHeaders . | |
| 401 * @return {!Object} Modified request headers. | |
| 402 * @private | |
| 403 */ | |
| 404 Authenticator.prototype.onBeforeSendHeaders_ = function(details) { | |
| 405 // deviceId_ is empty when we do not need to send it. For example, | |
| 406 // in case of device enrollment. | |
| 407 if (this.isNewGaiaFlowChromeOS && this.deviceId_) { | |
| 408 var headers = details.requestHeaders; | |
| 409 var found = false; | |
| 410 var deviceId = this.getGAIADeviceId_(); | |
| 411 | |
| 412 for (var i = 0, l = headers.length; i < l; ++i) { | |
| 413 if (headers[i].name == X_DEVICE_ID_HEADER) { | |
| 414 headers[i].value = deviceId; | |
| 415 found = true; | |
| 416 break; | |
| 417 } | |
| 418 } | |
| 419 if (!found) { | |
| 420 details.requestHeaders.push( | |
| 421 {name: X_DEVICE_ID_HEADER, value: deviceId}); | |
| 422 } | |
| 423 } | |
| 424 return { | |
| 425 requestHeaders: details.requestHeaders | |
| 426 }; | |
| 427 }; | |
| 428 | |
| 429 /** | |
| 430 * Returns true if given HTML5 message is received from the webview element. | 383 * Returns true if given HTML5 message is received from the webview element. |
| 431 * @param {object} e Payload of the received HTML5 message. | 384 * @param {object} e Payload of the received HTML5 message. |
| 432 */ | 385 */ |
| 433 Authenticator.prototype.isGaiaMessage = function(e) { | 386 Authenticator.prototype.isGaiaMessage = function(e) { |
| 434 if (!this.isWebviewEvent_(e)) | 387 if (!this.isWebviewEvent_(e)) |
| 435 return false; | 388 return false; |
| 436 | 389 |
| 437 // The event origin does not have a trailing slash. | 390 // The event origin does not have a trailing slash. |
| 438 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_.length - 1)) { | 391 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_.length - 1)) { |
| 439 return false; | 392 return false; |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 575 { | 528 { |
| 576 detail: { | 529 detail: { |
| 577 email: this.email_ || '', | 530 email: this.email_ || '', |
| 578 gaiaId: this.gaiaId_ || '', | 531 gaiaId: this.gaiaId_ || '', |
| 579 password: this.password_ || '', | 532 password: this.password_ || '', |
| 580 authCode: this.oauth_code_, | 533 authCode: this.oauth_code_, |
| 581 usingSAML: this.authFlow == AuthFlow.SAML, | 534 usingSAML: this.authFlow == AuthFlow.SAML, |
| 582 chooseWhatToSync: this.chooseWhatToSync_, | 535 chooseWhatToSync: this.chooseWhatToSync_, |
| 583 skipForNow: this.skipForNow_, | 536 skipForNow: this.skipForNow_, |
| 584 sessionIndex: this.sessionIndex_ || '', | 537 sessionIndex: this.sessionIndex_ || '', |
| 585 trusted: this.trusted_, | 538 trusted: this.trusted_ |
| 586 deviceId: this.deviceId_ || '' | |
| 587 } | 539 } |
| 588 })); | 540 })); |
| 589 this.clearCredentials_(); | 541 this.clearCredentials_(); |
| 590 }; | 542 }; |
| 591 | 543 |
| 592 /** | 544 /** |
| 593 * Invoked when |samlHandler_| fires 'insecureContentBlocked' event. | 545 * Invoked when |samlHandler_| fires 'insecureContentBlocked' event. |
| 594 * @private | 546 * @private |
| 595 */ | 547 */ |
| 596 Authenticator.prototype.onInsecureContentBlocked_ = function(e) { | 548 Authenticator.prototype.onInsecureContentBlocked_ = function(e) { |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 640 // mode, and then set it to 100% zoom. | 592 // mode, and then set it to 100% zoom. |
| 641 this.webview_.setZoomMode('per-view'); | 593 this.webview_.setZoomMode('per-view'); |
| 642 this.webview_.setZoom(1); | 594 this.webview_.setZoom(1); |
| 643 } | 595 } |
| 644 | 596 |
| 645 // Posts a message to IdP pages to initiate communication. | 597 // Posts a message to IdP pages to initiate communication. |
| 646 var currentUrl = this.webview_.src; | 598 var currentUrl = this.webview_.src; |
| 647 if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) { | 599 if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) { |
| 648 var msg = { | 600 var msg = { |
| 649 'method': 'handshake', | 601 'method': 'handshake', |
| 650 'deviceId': this.getGAIADeviceId_(), | |
| 651 }; | 602 }; |
| 652 | 603 |
| 653 this.webview_.contentWindow.postMessage(msg, currentUrl); | 604 this.webview_.contentWindow.postMessage(msg, currentUrl); |
| 654 } | 605 } |
| 655 }; | 606 }; |
| 656 | 607 |
| 657 /** | 608 /** |
| 658 * Invoked when the webview fails loading a page. | 609 * Invoked when the webview fails loading a page. |
| 659 * @private | 610 * @private |
| 660 */ | 611 */ |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 709 */ | 660 */ |
| 710 Authenticator.prototype.isWebviewEvent_ = function(e) { | 661 Authenticator.prototype.isWebviewEvent_ = function(e) { |
| 711 // Note: <webview> prints error message to console if |contentWindow| is not | 662 // Note: <webview> prints error message to console if |contentWindow| is not |
| 712 // defined. | 663 // defined. |
| 713 // TODO(dzhioev): remove the message. http://crbug.com/469522 | 664 // TODO(dzhioev): remove the message. http://crbug.com/469522 |
| 714 var webviewWindow = this.webview_.contentWindow; | 665 var webviewWindow = this.webview_.contentWindow; |
| 715 return !!webviewWindow && webviewWindow === e.source; | 666 return !!webviewWindow && webviewWindow === e.source; |
| 716 }; | 667 }; |
| 717 | 668 |
| 718 /** | 669 /** |
| 719 * Format deviceId for GAIA . | |
| 720 * @return {string} deviceId. | |
| 721 * @private | |
| 722 */ | |
| 723 Authenticator.prototype.getGAIADeviceId_ = function() { | |
| 724 // deviceId_ is empty when we do not need to send it. For example, | |
| 725 // in case of device enrollment. | |
| 726 if (!(this.isNewGaiaFlowChromeOS && this.deviceId_)) | |
| 727 return; | |
| 728 | |
| 729 if (this.sessionIsEphemeral_) | |
| 730 return EPHEMERAL_DEVICE_ID_PREFIX + this.deviceId_; | |
| 731 else | |
| 732 return this.deviceId_; | |
| 733 }; | |
| 734 | |
| 735 /** | |
| 736 * Informs Gaia of new deviceId to be used. | |
| 737 */ | |
| 738 Authenticator.prototype.updateDeviceId = function(deviceId) { | |
| 739 this.deviceId_ = deviceId; | |
| 740 var msg = { | |
| 741 'method': 'updateDeviceId', | |
| 742 'deviceId': this.getGAIADeviceId_(), | |
| 743 }; | |
| 744 | |
| 745 var currentUrl = this.webview_.src; | |
| 746 this.webview_.contentWindow.postMessage(msg, currentUrl); | |
| 747 }; | |
| 748 | |
| 749 /** | |
| 750 * The current auth flow of the hosted auth page. | 670 * The current auth flow of the hosted auth page. |
| 751 * @type {AuthFlow} | 671 * @type {AuthFlow} |
| 752 */ | 672 */ |
| 753 cr.defineProperty(Authenticator, 'authFlow'); | 673 cr.defineProperty(Authenticator, 'authFlow'); |
| 754 | 674 |
| 755 /** | 675 /** |
| 756 * The domain name of the current auth page. | 676 * The domain name of the current auth page. |
| 757 * @type {string} | 677 * @type {string} |
| 758 */ | 678 */ |
| 759 cr.defineProperty(Authenticator, 'authDomain'); | 679 cr.defineProperty(Authenticator, 'authDomain'); |
| 760 | 680 |
| 761 Authenticator.AuthFlow = AuthFlow; | 681 Authenticator.AuthFlow = AuthFlow; |
| 762 Authenticator.AuthMode = AuthMode; | 682 Authenticator.AuthMode = AuthMode; |
| 763 Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS; | 683 Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS; |
| 764 | 684 |
| 765 return { | 685 return { |
| 766 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old | 686 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old |
| 767 // iframe-based flow is deprecated. | 687 // iframe-based flow is deprecated. |
| 768 GaiaAuthHost: Authenticator, | 688 GaiaAuthHost: Authenticator, |
| 769 Authenticator: Authenticator | 689 Authenticator: Authenticator |
| 770 }; | 690 }; |
| 771 }); | 691 }); |
| OLD | NEW |