| 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 |
| 11 * cr.login.GaiaAuthHost.Listener as defined in this file. After initialization, | 11 * cr.login.GaiaAuthHost.Listener as defined in this file. After initialization, |
| 12 * call {@code load} to start the authentication flow. | 12 * call {@code load} to start the authentication flow. |
| 13 */ | 13 */ |
| 14 | 14 |
| 15 cr.define('cr.login', function() { | 15 cr.define('cr.login', function() { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 37 /** | 37 /** |
| 38 * The source URL parameter for the constrained signin flow. | 38 * The source URL parameter for the constrained signin flow. |
| 39 */ | 39 */ |
| 40 var CONSTRAINED_FLOW_SOURCE = 'chrome'; | 40 var CONSTRAINED_FLOW_SOURCE = 'chrome'; |
| 41 | 41 |
| 42 /** | 42 /** |
| 43 * Enum for the authorization mode, must match AuthMode defined in | 43 * Enum for the authorization mode, must match AuthMode defined in |
| 44 * chrome/browser/ui/webui/inline_login_ui.cc. | 44 * chrome/browser/ui/webui/inline_login_ui.cc. |
| 45 * @enum {number} | 45 * @enum {number} |
| 46 */ | 46 */ |
| 47 var AuthMode = { | 47 var AuthMode = {DEFAULT: 0, OFFLINE: 1, DESKTOP: 2}; |
| 48 DEFAULT: 0, | |
| 49 OFFLINE: 1, | |
| 50 DESKTOP: 2 | |
| 51 }; | |
| 52 | 48 |
| 53 /** | 49 /** |
| 54 * Enum for the authorization type. | 50 * Enum for the authorization type. |
| 55 * @enum {number} | 51 * @enum {number} |
| 56 */ | 52 */ |
| 57 var AuthFlow = { | 53 var AuthFlow = {DEFAULT: 0, SAML: 1}; |
| 58 DEFAULT: 0, | |
| 59 SAML: 1 | |
| 60 }; | |
| 61 | 54 |
| 62 /** | 55 /** |
| 63 * Supported Authenticator params. | 56 * Supported Authenticator params. |
| 64 * @type {!Array<string>} | 57 * @type {!Array<string>} |
| 65 * @const | 58 * @const |
| 66 */ | 59 */ |
| 67 var SUPPORTED_PARAMS = [ | 60 var SUPPORTED_PARAMS = [ |
| 68 'gaiaId', // Obfuscated GAIA ID to skip the email prompt page | 61 'gaiaId', // Obfuscated GAIA ID to skip the email prompt page |
| 69 // during the re-auth flow. | 62 // during the re-auth flow. |
| 70 'gaiaUrl', // Gaia url to use. | 63 'gaiaUrl', // Gaia url to use. |
| 71 'gaiaPath', // Gaia path to use without a leading slash. | 64 'gaiaPath', // Gaia path to use without a leading slash. |
| 72 'hl', // Language code for the user interface. | 65 'hl', // Language code for the user interface. |
| 73 'service', // Name of Gaia service. | 66 'service', // Name of Gaia service. |
| 74 'continueUrl', // Continue url to use. | 67 'continueUrl', // Continue url to use. |
| 75 'frameUrl', // Initial frame URL to use. If empty defaults to | 68 'frameUrl', // Initial frame URL to use. If empty defaults to |
| 76 // gaiaUrl. | 69 // gaiaUrl. |
| 77 'constrained', // Whether the extension is loaded in a constrained | 70 'constrained', // Whether the extension is loaded in a constrained |
| 78 // window. | 71 // window. |
| 79 'clientId', // Chrome client id. | 72 'clientId', // Chrome client id. |
| 80 'useEafe', // Whether to use EAFE. | 73 'useEafe', // Whether to use EAFE. |
| 81 'needPassword', // Whether the host is interested in getting a password. | 74 'needPassword', // Whether the host is interested in getting a password. |
| 82 // If this set to |false|, |confirmPasswordCallback| is | 75 // If this set to |false|, |confirmPasswordCallback| is |
| 83 // not called before dispatching |authCopleted|. | 76 // not called before dispatching |authCopleted|. |
| 84 // Default is |true|. | 77 // Default is |true|. |
| 85 'flow', // One of 'default', 'enterprise', or 'theftprotection'. | 78 'flow', // One of 'default', 'enterprise', or 'theftprotection'. |
| 86 'enterpriseDomain', // Domain in which hosting device is (or should be) | 79 'enterpriseDomain', // Domain in which hosting device is (or should be) |
| 87 // enrolled. | 80 // enrolled. |
| 88 'emailDomain', // Value used to prefill domain for email. | 81 'emailDomain', // Value used to prefill domain for email. |
| 89 'chromeType', // Type of Chrome OS device, e.g. "chromebox". | 82 'chromeType', // Type of Chrome OS device, e.g. "chromebox". |
| 90 'clientVersion', // Version of the Chrome build. | 83 'clientVersion', // Version of the Chrome build. |
| 91 'platformVersion', // Version of the OS build. | 84 'platformVersion', // Version of the OS build. |
| 92 'releaseChannel', // Installation channel. | 85 'releaseChannel', // Installation channel. |
| 93 'endpointGen', // Current endpoint generation. | 86 'endpointGen', // Current endpoint generation. |
| 94 'gapsCookie', // GAPS cookie | 87 'gapsCookie', // GAPS cookie |
| 95 | 88 |
| 96 // The email fields allow for the following possibilities: | 89 // The email fields allow for the following possibilities: |
| 97 // | 90 // |
| 98 // 1/ If 'email' is not supplied, then the email text field is blank and the | 91 // 1/ If 'email' is not supplied, then the email text field is blank and the |
| 99 // user must type an email to proceed. | 92 // user must type an email to proceed. |
| 100 // | 93 // |
| 101 // 2/ If 'email' is supplied, and 'readOnlyEmail' is truthy, then the email | 94 // 2/ If 'email' is supplied, and 'readOnlyEmail' is truthy, then the email |
| 102 // is hardcoded and the user cannot change it. The user is asked for | 95 // is hardcoded and the user cannot change it. The user is asked for |
| 103 // password. This is useful for re-auth scenarios, where chrome needs the | 96 // password. This is useful for re-auth scenarios, where chrome needs the |
| 104 // user to authenticate for a specific account and only that account. | 97 // user to authenticate for a specific account and only that account. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 119 * web pages. | 112 * web pages. |
| 120 * @constructor | 113 * @constructor |
| 121 */ | 114 */ |
| 122 function Authenticator(webview) { | 115 function Authenticator(webview) { |
| 123 this.webview_ = typeof webview == 'string' ? $(webview) : webview; | 116 this.webview_ = typeof webview == 'string' ? $(webview) : webview; |
| 124 assert(this.webview_); | 117 assert(this.webview_); |
| 125 | 118 |
| 126 this.isLoaded_ = false; | 119 this.isLoaded_ = false; |
| 127 this.email_ = null; | 120 this.email_ = null; |
| 128 this.password_ = null; | 121 this.password_ = null; |
| 129 this.gaiaId_ = null, | 122 this.gaiaId_ = null, this.sessionIndex_ = null; |
| 130 this.sessionIndex_ = null; | |
| 131 this.chooseWhatToSync_ = false; | 123 this.chooseWhatToSync_ = false; |
| 132 this.skipForNow_ = false; | 124 this.skipForNow_ = false; |
| 133 this.authFlow = AuthFlow.DEFAULT; | 125 this.authFlow = AuthFlow.DEFAULT; |
| 134 this.authDomain = ''; | 126 this.authDomain = ''; |
| 135 this.videoEnabled = false; | 127 this.videoEnabled = false; |
| 136 this.idpOrigin_ = null; | 128 this.idpOrigin_ = null; |
| 137 this.continueUrl_ = null; | 129 this.continueUrl_ = null; |
| 138 this.continueUrlWithoutParams_ = null; | 130 this.continueUrlWithoutParams_ = null; |
| 139 this.initialFrameUrl_ = null; | 131 this.initialFrameUrl_ = null; |
| 140 this.reloadUrl_ = null; | 132 this.reloadUrl_ = null; |
| 141 this.trusted_ = true; | 133 this.trusted_ = true; |
| 142 this.oauthCode_ = null; | 134 this.oauthCode_ = null; |
| 143 this.gapsCookie_ = null; | 135 this.gapsCookie_ = null; |
| 144 this.gapsCookieSent_ = false; | 136 this.gapsCookieSent_ = false; |
| 145 this.newGapsCookie_ = null; | 137 this.newGapsCookie_ = null; |
| 146 this.readyFired_ = false; | 138 this.readyFired_ = false; |
| 147 | 139 |
| 148 this.useEafe_ = false; | 140 this.useEafe_ = false; |
| 149 this.clientId_ = null; | 141 this.clientId_ = null; |
| 150 | 142 |
| 151 this.samlHandler_ = new cr.login.SamlHandler(this.webview_); | 143 this.samlHandler_ = new cr.login.SamlHandler(this.webview_); |
| 152 this.confirmPasswordCallback = null; | 144 this.confirmPasswordCallback = null; |
| 153 this.noPasswordCallback = null; | 145 this.noPasswordCallback = null; |
| 154 this.insecureContentBlockedCallback = null; | 146 this.insecureContentBlockedCallback = null; |
| 155 this.samlApiUsedCallback = null; | 147 this.samlApiUsedCallback = null; |
| 156 this.missingGaiaInfoCallback = null; | 148 this.missingGaiaInfoCallback = null; |
| 157 this.needPassword = true; | 149 this.needPassword = true; |
| 158 this.samlHandler_.addEventListener( | 150 this.samlHandler_.addEventListener( |
| 159 'insecureContentBlocked', | 151 'insecureContentBlocked', this.onInsecureContentBlocked_.bind(this)); |
| 160 this.onInsecureContentBlocked_.bind(this)); | |
| 161 this.samlHandler_.addEventListener( | 152 this.samlHandler_.addEventListener( |
| 162 'authPageLoaded', | 153 'authPageLoaded', this.onAuthPageLoaded_.bind(this)); |
| 163 this.onAuthPageLoaded_.bind(this)); | |
| 164 this.samlHandler_.addEventListener( | 154 this.samlHandler_.addEventListener( |
| 165 'videoEnabled', | 155 'videoEnabled', this.onVideoEnabled_.bind(this)); |
| 166 this.onVideoEnabled_.bind(this)); | |
| 167 this.samlHandler_.addEventListener( | 156 this.samlHandler_.addEventListener( |
| 168 'apiPasswordAdded', | 157 'apiPasswordAdded', this.onSamlApiPasswordAdded_.bind(this)); |
| 169 this.onSamlApiPasswordAdded_.bind(this)); | |
| 170 | 158 |
| 171 this.webview_.addEventListener('droplink', this.onDropLink_.bind(this)); | 159 this.webview_.addEventListener('droplink', this.onDropLink_.bind(this)); |
| 172 this.webview_.addEventListener( | 160 this.webview_.addEventListener('newwindow', this.onNewWindow_.bind(this)); |
| 173 'newwindow', this.onNewWindow_.bind(this)); | |
| 174 this.webview_.addEventListener( | 161 this.webview_.addEventListener( |
| 175 'contentload', this.onContentLoad_.bind(this)); | 162 'contentload', this.onContentLoad_.bind(this)); |
| 176 this.webview_.addEventListener( | 163 this.webview_.addEventListener('loadabort', this.onLoadAbort_.bind(this)); |
| 177 'loadabort', this.onLoadAbort_.bind(this)); | 164 this.webview_.addEventListener('loadstop', this.onLoadStop_.bind(this)); |
| 178 this.webview_.addEventListener( | 165 this.webview_.addEventListener('loadcommit', this.onLoadCommit_.bind(this)); |
| 179 'loadstop', this.onLoadStop_.bind(this)); | |
| 180 this.webview_.addEventListener( | |
| 181 'loadcommit', this.onLoadCommit_.bind(this)); | |
| 182 this.webview_.request.onCompleted.addListener( | 166 this.webview_.request.onCompleted.addListener( |
| 183 this.onRequestCompleted_.bind(this), | 167 this.onRequestCompleted_.bind(this), |
| 184 {urls: ['<all_urls>'], types: ['main_frame']}, | 168 {urls: ['<all_urls>'], types: ['main_frame']}, ['responseHeaders']); |
| 185 ['responseHeaders']); | |
| 186 this.webview_.request.onHeadersReceived.addListener( | 169 this.webview_.request.onHeadersReceived.addListener( |
| 187 this.onHeadersReceived_.bind(this), | 170 this.onHeadersReceived_.bind(this), |
| 188 {urls: ['<all_urls>'], types: ['main_frame', 'xmlhttprequest']}, | 171 {urls: ['<all_urls>'], types: ['main_frame', 'xmlhttprequest']}, |
| 189 ['responseHeaders']); | 172 ['responseHeaders']); |
| 190 window.addEventListener( | 173 window.addEventListener( |
| 191 'message', this.onMessageFromWebview_.bind(this), false); | 174 'message', this.onMessageFromWebview_.bind(this), false); |
| 192 window.addEventListener( | 175 window.addEventListener('focus', this.onFocus_.bind(this), false); |
| 193 'focus', this.onFocus_.bind(this), false); | 176 window.addEventListener('popstate', this.onPopState_.bind(this), false); |
| 194 window.addEventListener( | |
| 195 'popstate', this.onPopState_.bind(this), false); | |
| 196 } | 177 } |
| 197 | 178 |
| 198 Authenticator.prototype = Object.create(cr.EventTarget.prototype); | 179 Authenticator.prototype = Object.create(cr.EventTarget.prototype); |
| 199 | 180 |
| 200 /** | 181 /** |
| 201 * Reinitializes authentication parameters so that a failed login attempt | 182 * Reinitializes authentication parameters so that a failed login attempt |
| 202 * would not result in an infinite loop. | 183 * would not result in an infinite loop. |
| 203 */ | 184 */ |
| 204 Authenticator.prototype.resetStates = function() { | 185 Authenticator.prototype.resetStates = function() { |
| 205 this.isLoaded_ = false; | 186 this.isLoaded_ = false; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 248 this.clientId_ = data.clientId; | 229 this.clientId_ = data.clientId; |
| 249 this.gapsCookie_ = data.gapsCookie; | 230 this.gapsCookie_ = data.gapsCookie; |
| 250 this.gapsCookieSent_ = false; | 231 this.gapsCookieSent_ = false; |
| 251 this.newGapsCookie_ = null; | 232 this.newGapsCookie_ = null; |
| 252 this.dontResizeNonEmbeddedPages = data.dontResizeNonEmbeddedPages; | 233 this.dontResizeNonEmbeddedPages = data.dontResizeNonEmbeddedPages; |
| 253 | 234 |
| 254 this.initialFrameUrl_ = this.constructInitialFrameUrl_(data); | 235 this.initialFrameUrl_ = this.constructInitialFrameUrl_(data); |
| 255 this.reloadUrl_ = data.frameUrl || this.initialFrameUrl_; | 236 this.reloadUrl_ = data.frameUrl || this.initialFrameUrl_; |
| 256 // Don't block insecure content for desktop flow because it lands on | 237 // Don't block insecure content for desktop flow because it lands on |
| 257 // http. Otherwise, block insecure content as long as gaia is https. | 238 // http. Otherwise, block insecure content as long as gaia is https. |
| 258 this.samlHandler_.blockInsecureContent = authMode != AuthMode.DESKTOP && | 239 this.samlHandler_.blockInsecureContent = |
| 259 this.idpOrigin_.startsWith('https://'); | 240 authMode != AuthMode.DESKTOP && this.idpOrigin_.startsWith('https://'); |
| 260 this.needPassword = !('needPassword' in data) || data.needPassword; | 241 this.needPassword = !('needPassword' in data) || data.needPassword; |
| 261 | 242 |
| 262 if (this.isNewGaiaFlow) { | 243 if (this.isNewGaiaFlow) { |
| 263 this.webview_.contextMenus.onShow.addListener(function(e) { | 244 this.webview_.contextMenus.onShow.addListener(function(e) { |
| 264 e.preventDefault(); | 245 e.preventDefault(); |
| 265 }); | 246 }); |
| 266 | 247 |
| 267 if (!this.onBeforeSetHeadersSet_) { | 248 if (!this.onBeforeSetHeadersSet_) { |
| 268 this.onBeforeSetHeadersSet_ = true; | 249 this.onBeforeSetHeadersSet_ = true; |
| 269 var filterPrefix = this.idpOrigin_ + EMBEDDED_SETUP_CHROMEOS_ENDPOINT; | 250 var filterPrefix = this.idpOrigin_ + EMBEDDED_SETUP_CHROMEOS_ENDPOINT; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 285 Authenticator.prototype.reload = function() { | 266 Authenticator.prototype.reload = function() { |
| 286 this.resetStates(); | 267 this.resetStates(); |
| 287 this.webview_.src = this.reloadUrl_; | 268 this.webview_.src = this.reloadUrl_; |
| 288 this.isLoaded_ = true; | 269 this.isLoaded_ = true; |
| 289 }; | 270 }; |
| 290 | 271 |
| 291 Authenticator.prototype.constructInitialFrameUrl_ = function(data) { | 272 Authenticator.prototype.constructInitialFrameUrl_ = function(data) { |
| 292 if (data.doSamlRedirect) { | 273 if (data.doSamlRedirect) { |
| 293 var url = this.idpOrigin_ + SAML_REDIRECTION_PATH; | 274 var url = this.idpOrigin_ + SAML_REDIRECTION_PATH; |
| 294 url = appendParam(url, 'domain', data.enterpriseDomain); | 275 url = appendParam(url, 'domain', data.enterpriseDomain); |
| 295 url = appendParam(url, 'continue', data.gaiaUrl + | 276 url = appendParam( |
| 296 'o/oauth2/programmatic_auth?hl=' + data.hl + | 277 url, 'continue', data.gaiaUrl + 'o/oauth2/programmatic_auth?hl=' + |
| 297 '&scope=https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthLogin&' + | 278 data.hl + |
| 298 'client_id=' + encodeURIComponent(data.clientId) + | 279 '&scope=https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthLogin&' + |
| 299 '&access_type=offline'); | 280 'client_id=' + encodeURIComponent(data.clientId) + |
| 281 '&access_type=offline'); |
| 300 | 282 |
| 301 return url; | 283 return url; |
| 302 } | 284 } |
| 303 | 285 |
| 304 var path = data.gaiaPath; | 286 var path = data.gaiaPath; |
| 305 if (!path && this.isNewGaiaFlow) | 287 if (!path && this.isNewGaiaFlow) |
| 306 path = EMBEDDED_SETUP_CHROMEOS_ENDPOINT; | 288 path = EMBEDDED_SETUP_CHROMEOS_ENDPOINT; |
| 307 if (!path) | 289 if (!path) |
| 308 path = IDP_PATH; | 290 path = IDP_PATH; |
| 309 var url = this.idpOrigin_ + path; | 291 var url = this.idpOrigin_ + path; |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 462 }); | 444 }); |
| 463 // Removes "" around. | 445 // Removes "" around. |
| 464 this.email_ = signinDetails['email'].slice(1, -1); | 446 this.email_ = signinDetails['email'].slice(1, -1); |
| 465 this.gaiaId_ = signinDetails['obfuscatedid'].slice(1, -1); | 447 this.gaiaId_ = signinDetails['obfuscatedid'].slice(1, -1); |
| 466 this.sessionIndex_ = signinDetails['sessionindex']; | 448 this.sessionIndex_ = signinDetails['sessionindex']; |
| 467 } else if (headerName == LOCATION_HEADER) { | 449 } else if (headerName == LOCATION_HEADER) { |
| 468 // If the "choose what to sync" checkbox was clicked, then the continue | 450 // If the "choose what to sync" checkbox was clicked, then the continue |
| 469 // URL will contain a source=3 field. | 451 // URL will contain a source=3 field. |
| 470 var location = decodeURIComponent(header.value); | 452 var location = decodeURIComponent(header.value); |
| 471 this.chooseWhatToSync_ = !!location.match(/(\?|&)source=3($|&)/); | 453 this.chooseWhatToSync_ = !!location.match(/(\?|&)source=3($|&)/); |
| 472 } else if ( | 454 } else if (this.isNewGaiaFlow && headerName == SET_COOKIE_HEADER) { |
| 473 this.isNewGaiaFlow && headerName == SET_COOKIE_HEADER) { | |
| 474 var headerValue = header.value; | 455 var headerValue = header.value; |
| 475 if (headerValue.startsWith(OAUTH_CODE_COOKIE + '=')) { | 456 if (headerValue.startsWith(OAUTH_CODE_COOKIE + '=')) { |
| 476 this.oauthCode_ = | 457 this.oauthCode_ = |
| 477 headerValue.substring(OAUTH_CODE_COOKIE.length + 1).split(';')[0]; | 458 headerValue.substring(OAUTH_CODE_COOKIE.length + 1).split(';')[0]; |
| 478 } | 459 } |
| 479 if (headerValue.startsWith(GAPS_COOKIE + '=')) { | 460 if (headerValue.startsWith(GAPS_COOKIE + '=')) { |
| 480 this.newGapsCookie_ = | 461 this.newGapsCookie_ = |
| 481 headerValue.substring(GAPS_COOKIE.length + 1).split(';')[0]; | 462 headerValue.substring(GAPS_COOKIE.length + 1).split(';')[0]; |
| 482 } | 463 } |
| 483 } | 464 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 518 // We should re-send cookie if first request was unsuccessful (i.e. no new | 499 // We should re-send cookie if first request was unsuccessful (i.e. no new |
| 519 // GAPS cookie was received). | 500 // GAPS cookie was received). |
| 520 if (this.isNewGaiaFlow && this.gapsCookie_ && | 501 if (this.isNewGaiaFlow && this.gapsCookie_ && |
| 521 (!this.gapsCookieSent_ || !this.newGapsCookie_)) { | 502 (!this.gapsCookieSent_ || !this.newGapsCookie_)) { |
| 522 var headers = details.requestHeaders; | 503 var headers = details.requestHeaders; |
| 523 var found = false; | 504 var found = false; |
| 524 var gapsCookie = this.gapsCookie_; | 505 var gapsCookie = this.gapsCookie_; |
| 525 | 506 |
| 526 for (var i = 0, l = headers.length; i < l; ++i) { | 507 for (var i = 0, l = headers.length; i < l; ++i) { |
| 527 if (headers[i].name == COOKIE_HEADER) { | 508 if (headers[i].name == COOKIE_HEADER) { |
| 528 headers[i].value = this.updateCookieValue_(headers[i].value, | 509 headers[i].value = this.updateCookieValue_( |
| 529 GAPS_COOKIE, gapsCookie); | 510 headers[i].value, GAPS_COOKIE, gapsCookie); |
| 530 found = true; | 511 found = true; |
| 531 break; | 512 break; |
| 532 } | 513 } |
| 533 } | 514 } |
| 534 if (!found) { | 515 if (!found) { |
| 535 details.requestHeaders.push( | 516 details.requestHeaders.push( |
| 536 {name: COOKIE_HEADER, value: GAPS_COOKIE + '=' + gapsCookie}); | 517 {name: COOKIE_HEADER, value: GAPS_COOKIE + '=' + gapsCookie}); |
| 537 } | 518 } |
| 538 this.gapsCookieSent_ = true; | 519 this.gapsCookieSent_ = true; |
| 539 } | 520 } |
| 540 return { | 521 return {requestHeaders: details.requestHeaders}; |
| 541 requestHeaders: details.requestHeaders | |
| 542 }; | |
| 543 }; | 522 }; |
| 544 | 523 |
| 545 /** | 524 /** |
| 546 * Returns true if given HTML5 message is received from the webview element. | 525 * Returns true if given HTML5 message is received from the webview element. |
| 547 * @param {object} e Payload of the received HTML5 message. | 526 * @param {object} e Payload of the received HTML5 message. |
| 548 */ | 527 */ |
| 549 Authenticator.prototype.isGaiaMessage = function(e) { | 528 Authenticator.prototype.isGaiaMessage = function(e) { |
| 550 if (!this.isWebviewEvent_(e)) | 529 if (!this.isWebviewEvent_(e)) |
| 551 return false; | 530 return false; |
| 552 | 531 |
| 553 // The event origin does not have a trailing slash. | 532 // The event origin does not have a trailing slash. |
| 554 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_.length - 1)) { | 533 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_.length - 1)) { |
| 555 return false; | 534 return false; |
| 556 } | 535 } |
| 557 | 536 |
| 558 // EAFE passes back auth code via message. | 537 // EAFE passes back auth code via message. |
| 559 if (this.useEafe_ && | 538 if (this.useEafe_ && typeof e.data == 'object' && |
| 560 typeof e.data == 'object' && | |
| 561 e.data.hasOwnProperty('authorizationCode')) { | 539 e.data.hasOwnProperty('authorizationCode')) { |
| 562 assert(!this.oauthCode_); | 540 assert(!this.oauthCode_); |
| 563 this.oauthCode_ = e.data.authorizationCode; | 541 this.oauthCode_ = e.data.authorizationCode; |
| 564 this.dispatchEvent( | 542 this.dispatchEvent(new CustomEvent( |
| 565 new CustomEvent('authCompleted', | 543 'authCompleted', |
| 566 { | 544 {detail: {authCodeOnly: true, authCode: this.oauthCode_}})); |
| 567 detail: { | |
| 568 authCodeOnly: true, | |
| 569 authCode: this.oauthCode_ | |
| 570 } | |
| 571 })); | |
| 572 return; | 545 return; |
| 573 } | 546 } |
| 574 | 547 |
| 575 // Gaia messages must be an object with 'method' property. | 548 // Gaia messages must be an object with 'method' property. |
| 576 if (typeof e.data != 'object' || !e.data.hasOwnProperty('method')) { | 549 if (typeof e.data != 'object' || !e.data.hasOwnProperty('method')) { |
| 577 return false; | 550 return false; |
| 578 } | 551 } |
| 579 return true; | 552 return true; |
| 580 }; | 553 }; |
| 581 | 554 |
| 582 /** | 555 /** |
| 583 * Invoked when an HTML5 message is received from the webview element. | 556 * Invoked when an HTML5 message is received from the webview element. |
| 584 * @param {object} e Payload of the received HTML5 message. | 557 * @param {object} e Payload of the received HTML5 message. |
| 585 * @private | 558 * @private |
| 586 */ | 559 */ |
| 587 Authenticator.prototype.onMessageFromWebview_ = function(e) { | 560 Authenticator.prototype.onMessageFromWebview_ = function(e) { |
| 588 if (!this.isGaiaMessage(e)) | 561 if (!this.isGaiaMessage(e)) |
| 589 return; | 562 return; |
| 590 | 563 |
| 591 var msg = e.data; | 564 var msg = e.data; |
| 592 if (msg.method == 'attemptLogin') { | 565 if (msg.method == 'attemptLogin') { |
| 593 this.email_ = msg.email; | 566 this.email_ = msg.email; |
| 594 if (this.authMode == AuthMode.DESKTOP) | 567 if (this.authMode == AuthMode.DESKTOP) |
| 595 this.password_ = msg.password; | 568 this.password_ = msg.password; |
| 596 | 569 |
| 597 this.chooseWhatToSync_ = msg.chooseWhatToSync; | 570 this.chooseWhatToSync_ = msg.chooseWhatToSync; |
| 598 // We need to dispatch only first event, before user enters password. | 571 // We need to dispatch only first event, before user enters password. |
| 599 this.dispatchEvent( | 572 this.dispatchEvent(new CustomEvent('attemptLogin', {detail: msg.email})); |
| 600 new CustomEvent('attemptLogin', {detail: msg.email})); | |
| 601 } else if (msg.method == 'dialogShown') { | 573 } else if (msg.method == 'dialogShown') { |
| 602 this.dispatchEvent(new Event('dialogShown')); | 574 this.dispatchEvent(new Event('dialogShown')); |
| 603 } else if (msg.method == 'dialogHidden') { | 575 } else if (msg.method == 'dialogHidden') { |
| 604 this.dispatchEvent(new Event('dialogHidden')); | 576 this.dispatchEvent(new Event('dialogHidden')); |
| 605 } else if (msg.method == 'backButton') { | 577 } else if (msg.method == 'backButton') { |
| 606 this.dispatchEvent(new CustomEvent('backButton', {detail: msg.show})); | 578 this.dispatchEvent(new CustomEvent('backButton', {detail: msg.show})); |
| 607 } else if (msg.method == 'showView') { | 579 } else if (msg.method == 'showView') { |
| 608 this.dispatchEvent(new Event('showView')); | 580 this.dispatchEvent(new Event('showView')); |
| 609 } else if (msg.method == 'identifierEntered') { | 581 } else if (msg.method == 'identifierEntered') { |
| 610 this.dispatchEvent(new CustomEvent( | 582 this.dispatchEvent(new CustomEvent( |
| 611 'identifierEntered', | 583 'identifierEntered', |
| 612 {detail: {accountIdentifier: msg.accountIdentifier}})); | 584 {detail: {accountIdentifier: msg.accountIdentifier}})); |
| 613 } else { | 585 } else { |
| 614 console.warn('Unrecognized message from GAIA: ' + msg.method); | 586 console.warn('Unrecognized message from GAIA: ' + msg.method); |
| 615 } | 587 } |
| 616 }; | 588 }; |
| 617 | 589 |
| 618 /** | 590 /** |
| 619 * Invoked by the hosting page to verify the Saml password. | 591 * Invoked by the hosting page to verify the Saml password. |
| 620 */ | 592 */ |
| 621 Authenticator.prototype.verifyConfirmedPassword = function(password) { | 593 Authenticator.prototype.verifyConfirmedPassword = function(password) { |
| 622 if (!this.samlHandler_.verifyConfirmedPassword(password)) { | 594 if (!this.samlHandler_.verifyConfirmedPassword(password)) { |
| 623 // Invoke confirm password callback asynchronously because the | 595 // Invoke confirm password callback asynchronously because the |
| 624 // verification was based on messages and caller (GaiaSigninScreen) | 596 // verification was based on messages and caller (GaiaSigninScreen) |
| 625 // does not expect it to be called immediately. | 597 // does not expect it to be called immediately. |
| 626 // TODO(xiyuan): Change to synchronous call when iframe based code | 598 // TODO(xiyuan): Change to synchronous call when iframe based code |
| 627 // is removed. | 599 // is removed. |
| 628 var invokeConfirmPassword = (function() { | 600 var invokeConfirmPassword = |
| 629 this.confirmPasswordCallback(this.email_, | 601 (function() { |
| 630 this.samlHandler_.scrapedPasswordCount); | 602 this.confirmPasswordCallback( |
| 631 }).bind(this); | 603 this.email_, this.samlHandler_.scrapedPasswordCount); |
| 604 }).bind(this); |
| 632 window.setTimeout(invokeConfirmPassword, 0); | 605 window.setTimeout(invokeConfirmPassword, 0); |
| 633 return; | 606 return; |
| 634 } | 607 } |
| 635 | 608 |
| 636 this.password_ = password; | 609 this.password_ = password; |
| 637 this.onAuthCompleted_(); | 610 this.onAuthCompleted_(); |
| 638 }; | 611 }; |
| 639 | 612 |
| 640 /** | 613 /** |
| 641 * Check Saml flow and start password confirmation flow if needed. Otherwise, | 614 * Check Saml flow and start password confirmation flow if needed. Otherwise, |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 676 // If we scraped exactly one password, we complete the authentication | 649 // If we scraped exactly one password, we complete the authentication |
| 677 // right away. | 650 // right away. |
| 678 this.password_ = this.samlHandler_.firstScrapedPassword; | 651 this.password_ = this.samlHandler_.firstScrapedPassword; |
| 679 this.onAuthCompleted_(); | 652 this.onAuthCompleted_(); |
| 680 return; | 653 return; |
| 681 } | 654 } |
| 682 | 655 |
| 683 if (this.confirmPasswordCallback) { | 656 if (this.confirmPasswordCallback) { |
| 684 // Confirm scraped password. The flow follows in | 657 // Confirm scraped password. The flow follows in |
| 685 // verifyConfirmedPassword. | 658 // verifyConfirmedPassword. |
| 686 this.confirmPasswordCallback(this.email_, | 659 this.confirmPasswordCallback( |
| 687 this.samlHandler_.scrapedPasswordCount); | 660 this.email_, this.samlHandler_.scrapedPasswordCount); |
| 688 return; | 661 return; |
| 689 } | 662 } |
| 690 } | 663 } |
| 691 | 664 |
| 692 this.onAuthCompleted_(); | 665 this.onAuthCompleted_(); |
| 693 }; | 666 }; |
| 694 | 667 |
| 695 /** | 668 /** |
| 696 * Invoked to complete the authentication using the password the user enters | 669 * Invoked to complete the authentication using the password the user enters |
| 697 * manually for non-principals API SAML IdPs that we couldn't scrape their | 670 * manually for non-principals API SAML IdPs that we couldn't scrape their |
| 698 * password input. | 671 * password input. |
| 699 */ | 672 */ |
| 700 Authenticator.prototype.completeAuthWithManualPassword = function(password) { | 673 Authenticator.prototype.completeAuthWithManualPassword = function(password) { |
| 701 this.password_ = password; | 674 this.password_ = password; |
| 702 this.onAuthCompleted_(); | 675 this.onAuthCompleted_(); |
| 703 }; | 676 }; |
| 704 | 677 |
| 705 /** | 678 /** |
| 706 * Invoked to process authentication completion. | 679 * Invoked to process authentication completion. |
| 707 * @private | 680 * @private |
| 708 */ | 681 */ |
| 709 Authenticator.prototype.onAuthCompleted_ = function() { | 682 Authenticator.prototype.onAuthCompleted_ = function() { |
| 710 assert(this.skipForNow_ || | 683 assert( |
| 711 (this.email_ && this.gaiaId_ && this.sessionIndex_)); | 684 this.skipForNow_ || |
| 685 (this.email_ && this.gaiaId_ && this.sessionIndex_)); |
| 712 this.dispatchEvent(new CustomEvent( | 686 this.dispatchEvent(new CustomEvent( |
| 713 'authCompleted', | 687 'authCompleted', |
| 714 // TODO(rsorokin): get rid of the stub values. | 688 // TODO(rsorokin): get rid of the stub values. |
| 715 { | 689 { |
| 716 detail: { | 690 detail: { |
| 717 email: this.email_ || '', | 691 email: this.email_ || '', |
| 718 gaiaId: this.gaiaId_ || '', | 692 gaiaId: this.gaiaId_ || '', |
| 719 password: this.password_ || '', | 693 password: this.password_ || '', |
| 720 authCode: this.oauthCode_, | 694 authCode: this.oauthCode_, |
| 721 usingSAML: this.authFlow == AuthFlow.SAML, | 695 usingSAML: this.authFlow == AuthFlow.SAML, |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 824 } else if (currentUrl == BLANK_PAGE_URL) { | 798 } else if (currentUrl == BLANK_PAGE_URL) { |
| 825 this.fireReadyEvent_(); | 799 this.fireReadyEvent_(); |
| 826 } | 800 } |
| 827 }; | 801 }; |
| 828 | 802 |
| 829 /** | 803 /** |
| 830 * Invoked when the webview fails loading a page. | 804 * Invoked when the webview fails loading a page. |
| 831 * @private | 805 * @private |
| 832 */ | 806 */ |
| 833 Authenticator.prototype.onLoadAbort_ = function(e) { | 807 Authenticator.prototype.onLoadAbort_ = function(e) { |
| 834 this.dispatchEvent(new CustomEvent('loadAbort', | 808 this.dispatchEvent( |
| 835 {detail: {error: e.reason, src: e.url}})); | 809 new CustomEvent('loadAbort', {detail: {error: e.reason, src: e.url}})); |
| 836 }; | 810 }; |
| 837 | 811 |
| 838 /** | 812 /** |
| 839 * Invoked when the webview finishes loading a page. | 813 * Invoked when the webview finishes loading a page. |
| 840 * @private | 814 * @private |
| 841 */ | 815 */ |
| 842 Authenticator.prototype.onLoadStop_ = function(e) { | 816 Authenticator.prototype.onLoadStop_ = function(e) { |
| 843 // Sends client id to EAFE on every loadstop after a small timeout. This is | 817 // Sends client id to EAFE on every loadstop after a small timeout. This is |
| 844 // needed because EAFE sits behind SSO and initialize asynchrounouly | 818 // needed because EAFE sits behind SSO and initialize asynchrounouly |
| 845 // and we don't know for sure when it is loaded and ready to listen | 819 // and we don't know for sure when it is loaded and ready to listen |
| 846 // for message. The postMessage is guarded by EAFE's origin. | 820 // for message. The postMessage is guarded by EAFE's origin. |
| 847 if (this.useEafe_) { | 821 if (this.useEafe_) { |
| 848 // An arbitrary small timeout for delivering the initial message. | 822 // An arbitrary small timeout for delivering the initial message. |
| 849 var EAFE_INITIAL_MESSAGE_DELAY_IN_MS = 500; | 823 var EAFE_INITIAL_MESSAGE_DELAY_IN_MS = 500; |
| 850 window.setTimeout((function() { | 824 window.setTimeout( |
| 851 var msg = { | 825 (function() { |
| 852 'clientId': this.clientId_ | 826 var msg = {'clientId': this.clientId_}; |
| 853 }; | 827 this.webview_.contentWindow.postMessage(msg, this.idpOrigin_); |
| 854 this.webview_.contentWindow.postMessage(msg, this.idpOrigin_); | 828 }).bind(this), |
| 855 }).bind(this), EAFE_INITIAL_MESSAGE_DELAY_IN_MS); | 829 EAFE_INITIAL_MESSAGE_DELAY_IN_MS); |
| 856 } | 830 } |
| 857 }; | 831 }; |
| 858 | 832 |
| 859 /** | 833 /** |
| 860 * Invoked when the webview navigates withing the current document. | 834 * Invoked when the webview navigates withing the current document. |
| 861 * @private | 835 * @private |
| 862 */ | 836 */ |
| 863 Authenticator.prototype.onLoadCommit_ = function(e) { | 837 Authenticator.prototype.onLoadCommit_ = function(e) { |
| 864 if (this.oauthCode_) | 838 if (this.oauthCode_) |
| 865 this.maybeCompleteAuth_(); | 839 this.maybeCompleteAuth_(); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 899 Authenticator.AuthMode = AuthMode; | 873 Authenticator.AuthMode = AuthMode; |
| 900 Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS; | 874 Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS; |
| 901 | 875 |
| 902 return { | 876 return { |
| 903 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old | 877 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old |
| 904 // iframe-based flow is deprecated. | 878 // iframe-based flow is deprecated. |
| 905 GaiaAuthHost: Authenticator, | 879 GaiaAuthHost: Authenticator, |
| 906 Authenticator: Authenticator | 880 Authenticator: Authenticator |
| 907 }; | 881 }; |
| 908 }); | 882 }); |
| OLD | NEW |