Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(334)

Side by Side Diff: chrome/browser/resources/gaia_auth_host/authenticator.js

Issue 1229883003: ChromeOS: should send old user GAPS cookie to GAIA on user reauthentication. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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() {
16 'use strict'; 16 'use strict';
17 17
18 // TODO(rogerta): should use gaia URL from GaiaUrls::gaia_url() instead 18 // TODO(rogerta): should use gaia URL from GaiaUrls::gaia_url() instead
19 // of hardcoding the prod URL here. As is, this does not work with staging 19 // of hardcoding the prod URL here. As is, this does not work with staging
20 // environments. 20 // environments.
21 var IDP_ORIGIN = 'https://accounts.google.com/'; 21 var IDP_ORIGIN = 'https://accounts.google.com/';
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 COOKIE_HEADER = 'cookie';
28 var SET_COOKIE_HEADER = 'set-cookie'; 29 var SET_COOKIE_HEADER = 'set-cookie';
29 var OAUTH_CODE_COOKIE = 'oauth_code'; 30 var OAUTH_CODE_COOKIE = 'oauth_code';
31 var GAPS_COOKIE = 'GAPS';
30 var SERVICE_ID = 'chromeoslogin'; 32 var SERVICE_ID = 'chromeoslogin';
31 var EMBEDDED_SETUP_CHROMEOS_ENDPOINT = 'embedded/setup/chromeos'; 33 var EMBEDDED_SETUP_CHROMEOS_ENDPOINT = 'embedded/setup/chromeos';
32 34
33 /** 35 /**
34 * The source URL parameter for the constrained signin flow. 36 * The source URL parameter for the constrained signin flow.
35 */ 37 */
36 var CONSTRAINED_FLOW_SOURCE = 'chrome'; 38 var CONSTRAINED_FLOW_SOURCE = 'chrome';
37 39
38 /** 40 /**
39 * Enum for the authorization mode, must match AuthMode defined in 41 * Enum for the authorization mode, must match AuthMode defined in
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
80 // not called before dispatching |authCopleted|. 82 // not called before dispatching |authCopleted|.
81 // Default is |true|. 83 // Default is |true|.
82 'flow', // One of 'default', 'enterprise', or 'theftprotection'. 84 'flow', // One of 'default', 'enterprise', or 'theftprotection'.
83 'enterpriseDomain', // Domain in which hosting device is (or should be) 85 'enterpriseDomain', // Domain in which hosting device is (or should be)
84 // enrolled. 86 // enrolled.
85 'emailDomain', // Value used to prefill domain for email. 87 'emailDomain', // Value used to prefill domain for email.
86 'clientVersion', // Version of the Chrome build. 88 'clientVersion', // Version of the Chrome build.
87 'platformVersion', // Version of the OS build. 89 'platformVersion', // Version of the OS build.
88 'releaseChannel', // Installation channel. 90 'releaseChannel', // Installation channel.
89 'endpointGen', // Current endpoint generation. 91 'endpointGen', // Current endpoint generation.
92 'gapsCookie', // GAPS cookie
90 ]; 93 ];
91 94
92 /** 95 /**
93 * Initializes the authenticator component. 96 * Initializes the authenticator component.
94 * @param {webview|string} webview The webview element or its ID to host IdP 97 * @param {webview|string} webview The webview element or its ID to host IdP
95 * web pages. 98 * web pages.
96 * @constructor 99 * @constructor
97 */ 100 */
98 function Authenticator(webview) { 101 function Authenticator(webview) {
99 this.webview_ = typeof webview == 'string' ? $(webview) : webview; 102 this.webview_ = typeof webview == 'string' ? $(webview) : webview;
100 assert(this.webview_); 103 assert(this.webview_);
101 104
102 this.email_ = null; 105 this.email_ = null;
103 this.password_ = null; 106 this.password_ = null;
104 this.gaiaId_ = null, 107 this.gaiaId_ = null,
105 this.sessionIndex_ = null; 108 this.sessionIndex_ = null;
106 this.chooseWhatToSync_ = false; 109 this.chooseWhatToSync_ = false;
107 this.skipForNow_ = false; 110 this.skipForNow_ = false;
108 this.authFlow = AuthFlow.DEFAULT; 111 this.authFlow = AuthFlow.DEFAULT;
109 this.authDomain = ''; 112 this.authDomain = '';
110 this.loaded_ = false; 113 this.loaded_ = false;
111 this.idpOrigin_ = null; 114 this.idpOrigin_ = null;
112 this.continueUrl_ = null; 115 this.continueUrl_ = null;
113 this.continueUrlWithoutParams_ = null; 116 this.continueUrlWithoutParams_ = null;
114 this.initialFrameUrl_ = null; 117 this.initialFrameUrl_ = null;
115 this.reloadUrl_ = null; 118 this.reloadUrl_ = null;
116 this.trusted_ = true; 119 this.trusted_ = true;
117 this.oauth_code_ = null; 120 this.oauth_code_ = null;
121 this.gaps_cookie_ = null;
xiyuan 2015/07/09 23:08:29 nit: gaps_cookie_ -> gapsCookie_ JS variable name
Alexander Alekseev 2015/07/09 23:22:40 Done.
122 this.gaps_cookie_sent_ = false;
123 this.new_gaps_cookie_ = null;
118 124
119 this.useEafe_ = false; 125 this.useEafe_ = false;
120 this.clientId_ = null; 126 this.clientId_ = null;
121 127
122 this.samlHandler_ = new cr.login.SamlHandler(this.webview_); 128 this.samlHandler_ = new cr.login.SamlHandler(this.webview_);
123 this.confirmPasswordCallback = null; 129 this.confirmPasswordCallback = null;
124 this.noPasswordCallback = null; 130 this.noPasswordCallback = null;
125 this.insecureContentBlockedCallback = null; 131 this.insecureContentBlockedCallback = null;
126 this.samlApiUsedCallback = null; 132 this.samlApiUsedCallback = null;
127 this.missingGaiaInfoCallback = null; 133 this.missingGaiaInfoCallback = null;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
164 170
165 /** 171 /**
166 * Reinitializes authentication parameters so that a failed login attempt 172 * Reinitializes authentication parameters so that a failed login attempt
167 * would not result in an infinite loop. 173 * would not result in an infinite loop.
168 */ 174 */
169 Authenticator.prototype.clearCredentials_ = function() { 175 Authenticator.prototype.clearCredentials_ = function() {
170 this.email_ = null; 176 this.email_ = null;
171 this.gaiaId_ = null; 177 this.gaiaId_ = null;
172 this.password_ = null; 178 this.password_ = null;
173 this.oauth_code_ = null; 179 this.oauth_code_ = null;
180 this.gaps_cookie_ = null;
181 this.gaps_cookie_sent_ = false;
182 this.new_gaps_cookie_ = null;
174 this.chooseWhatToSync_ = false; 183 this.chooseWhatToSync_ = false;
175 this.skipForNow_ = false; 184 this.skipForNow_ = false;
176 this.sessionIndex_ = null; 185 this.sessionIndex_ = null;
177 this.trusted_ = true; 186 this.trusted_ = true;
178 this.authFlow = AuthFlow.DEFAULT; 187 this.authFlow = AuthFlow.DEFAULT;
179 this.samlHandler_.reset(); 188 this.samlHandler_.reset();
180 }; 189 };
181 190
182 /** 191 /**
183 * Loads the authenticator component with the given parameters. 192 * Loads the authenticator component with the given parameters.
(...skipping 18 matching lines...) Expand all
202 // Don't block insecure content for desktop flow because it lands on 211 // Don't block insecure content for desktop flow because it lands on
203 // http. Otherwise, block insecure content as long as gaia is https. 212 // http. Otherwise, block insecure content as long as gaia is https.
204 this.samlHandler_.blockInsecureContent = authMode != AuthMode.DESKTOP && 213 this.samlHandler_.blockInsecureContent = authMode != AuthMode.DESKTOP &&
205 this.idpOrigin_.indexOf('https://') == 0; 214 this.idpOrigin_.indexOf('https://') == 0;
206 this.needPassword = !('needPassword' in data) || data.needPassword; 215 this.needPassword = !('needPassword' in data) || data.needPassword;
207 216
208 if (this.isNewGaiaFlowChromeOS) { 217 if (this.isNewGaiaFlowChromeOS) {
209 this.webview_.contextMenus.onShow.addListener(function(e) { 218 this.webview_.contextMenus.onShow.addListener(function(e) {
210 e.preventDefault(); 219 e.preventDefault();
211 }); 220 });
221
222 var filterPrefix = this.idpOrigin_ + EMBEDDED_SETUP_CHROMEOS_ENDPOINT;
223 if (!this.onBeforeSetHeadersSet_) {
224 this.onBeforeSetHeadersSet_ = true;
225 this.webview_.request.onBeforeSendHeaders.addListener(
xiyuan 2015/07/09 23:08:29 Why this code lives here instead of with other req
Alexander Alekseev 2015/07/09 23:22:40 Because it depends on load parameter (line 199):
xiyuan 2015/07/09 23:30:05 I see. Could you add a comment to document why thi
Alexander Alekseev 2015/07/09 23:43:22 Done.
226 this.onBeforeSendHeaders_.bind(this),
227 {urls: [filterPrefix + '?*', filterPrefix + '/*']},
228 ['requestHeaders', 'blocking']);
229 }
212 } 230 }
213 231
214 this.webview_.src = this.reloadUrl_; 232 this.webview_.src = this.reloadUrl_;
215 }; 233 };
216 234
217 /** 235 /**
218 * Reloads the authenticator component. 236 * Reloads the authenticator component.
219 */ 237 */
220 Authenticator.prototype.reload = function() { 238 Authenticator.prototype.reload = function() {
221 this.clearCredentials_(); 239 this.clearCredentials_();
(...skipping 17 matching lines...) Expand all
239 if (data.enterpriseDomain) 257 if (data.enterpriseDomain)
240 url = appendParam(url, 'manageddomain', data.enterpriseDomain); 258 url = appendParam(url, 'manageddomain', data.enterpriseDomain);
241 if (data.clientVersion) 259 if (data.clientVersion)
242 url = appendParam(url, 'client_version', data.clientVersion); 260 url = appendParam(url, 'client_version', data.clientVersion);
243 if (data.platformVersion) 261 if (data.platformVersion)
244 url = appendParam(url, 'platform_version', data.platformVersion); 262 url = appendParam(url, 'platform_version', data.platformVersion);
245 if (data.releaseChannel) 263 if (data.releaseChannel)
246 url = appendParam(url, 'release_channel', data.releaseChannel); 264 url = appendParam(url, 'release_channel', data.releaseChannel);
247 if (data.endpointGen) 265 if (data.endpointGen)
248 url = appendParam(url, 'endpoint_gen', data.endpointGen); 266 url = appendParam(url, 'endpoint_gen', data.endpointGen);
267 this.gaps_cookie_ = data.gapsCookie;
268 this.gaps_cookie_sent_ = false;
269 this.new_gaps_cookie_ = null;
249 } else { 270 } else {
250 url = appendParam(url, 'continue', this.continueUrl_); 271 url = appendParam(url, 'continue', this.continueUrl_);
251 url = appendParam(url, 'service', data.service || SERVICE_ID); 272 url = appendParam(url, 'service', data.service || SERVICE_ID);
252 } 273 }
253 if (data.hl) 274 if (data.hl)
254 url = appendParam(url, 'hl', data.hl); 275 url = appendParam(url, 'hl', data.hl);
255 if (data.gaiaId) 276 if (data.gaiaId)
256 url = appendParam(url, 'user_id', data.gaiaId); 277 url = appendParam(url, 'user_id', data.gaiaId);
257 if (data.email) 278 if (data.email)
258 url = appendParam(url, 'Email', data.email); 279 url = appendParam(url, 'Email', data.email);
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 // URL will contain a source=3 field. 389 // URL will contain a source=3 field.
369 var location = decodeURIComponent(header.value); 390 var location = decodeURIComponent(header.value);
370 this.chooseWhatToSync_ = !!location.match(/(\?|&)source=3($|&)/); 391 this.chooseWhatToSync_ = !!location.match(/(\?|&)source=3($|&)/);
371 } else if ( 392 } else if (
372 this.isNewGaiaFlowChromeOS && headerName == SET_COOKIE_HEADER) { 393 this.isNewGaiaFlowChromeOS && headerName == SET_COOKIE_HEADER) {
373 var headerValue = header.value; 394 var headerValue = header.value;
374 if (headerValue.indexOf(OAUTH_CODE_COOKIE + '=', 0) == 0) { 395 if (headerValue.indexOf(OAUTH_CODE_COOKIE + '=', 0) == 0) {
375 this.oauth_code_ = 396 this.oauth_code_ =
376 headerValue.substring(OAUTH_CODE_COOKIE.length + 1).split(';')[0]; 397 headerValue.substring(OAUTH_CODE_COOKIE.length + 1).split(';')[0];
377 } 398 }
399 if (headerValue.indexOf(GAPS_COOKIE + '=', 0) == 0) {
400 this.new_gaps_cookie_ =
401 headerValue.substring(GAPS_COOKIE.length + 1).split(';')[0];
402 }
378 } 403 }
379 } 404 }
380 }; 405 };
381 406
382 /** 407 /**
408 * This method replaces cookie value in cookie header.
409 * @param@ {header_value} Original string value of Cookie header.
xiyuan 2015/07/09 23:08:29 Wrong JSDoc format here and below. Should be some
Alexander Alekseev 2015/07/09 23:22:40 Done.
410 * @param@ {cookie_name} Name of cookie to be replaced.
411 * @param@ {cookie_value} New cookie value.
412 * @return {string} New Cookie header value.
413 * @private
414 */
415 Authenticator.prototype.updateCookieValue_ = function(
416 header_value, cookie_name, cookie_value) {
417 var cookies = header_value.split(/\s*;\s*/);
418 var found = false;
419 for (var i = 0; i < cookies.length; ++i) {
420 if (cookies[i].indexOf(cookie_name + '=', 0) == 0) {
421 found = true;
422 cookies[i] = cookie_name + '=' + cookie_value;
423 break;
424 }
425 }
426 if (!found) {
427 cookies.push(cookie_name + '=' + cookie_value);
428 }
429 return cookies.join('; ');
430 };
431
432 /**
433 * Handler for webView.request.onBeforeSendHeaders .
434 * @return {!Object} Modified request headers.
435 * @private
436 */
437 Authenticator.prototype.onBeforeSendHeaders_ = function(details) {
438 if (this.isNewGaiaFlowChromeOS && this.gaps_cookie_ &&
439 !this.gaps_cookie_sent_) {
440 var headers = details.requestHeaders;
441 var found = false;
442 var gapsCookie = this.gaps_cookie_;
443
444 for (var i = 0, l = headers.length; i < l; ++i) {
445 if (headers[i].name == COOKIE_HEADER) {
446 headers[i].value = this.updateCookieValue_(headers[i].value,
447 GAPS_COOKIE, gapsCookie);
448 found = true;
449 break;
450 }
451 }
452 if (!found) {
453 details.requestHeaders.push(
454 {name: COOKIE_HEADER, value: GAPS_COOKIE + '=' + gapsCookie});
455 }
456 this.gaps_cookie_sent_ = true;
457 }
458 return {
459 requestHeaders: details.requestHeaders
460 };
461 };
462
463 /**
383 * Returns true if given HTML5 message is received from the webview element. 464 * Returns true if given HTML5 message is received from the webview element.
384 * @param {object} e Payload of the received HTML5 message. 465 * @param {object} e Payload of the received HTML5 message.
385 */ 466 */
386 Authenticator.prototype.isGaiaMessage = function(e) { 467 Authenticator.prototype.isGaiaMessage = function(e) {
387 if (!this.isWebviewEvent_(e)) 468 if (!this.isWebviewEvent_(e))
388 return false; 469 return false;
389 470
390 // The event origin does not have a trailing slash. 471 // The event origin does not have a trailing slash.
391 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_.length - 1)) { 472 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_.length - 1)) {
392 return false; 473 return false;
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
515 this.onAuthCompleted_(); 596 this.onAuthCompleted_();
516 }; 597 };
517 598
518 /** 599 /**
519 * Invoked to process authentication completion. 600 * Invoked to process authentication completion.
520 * @private 601 * @private
521 */ 602 */
522 Authenticator.prototype.onAuthCompleted_ = function() { 603 Authenticator.prototype.onAuthCompleted_ = function() {
523 assert(this.skipForNow_ || 604 assert(this.skipForNow_ ||
524 (this.email_ && this.gaiaId_ && this.sessionIndex_)); 605 (this.email_ && this.gaiaId_ && this.sessionIndex_));
525 this.dispatchEvent( 606 this.dispatchEvent(new CustomEvent(
526 new CustomEvent('authCompleted', 607 'authCompleted',
527 // TODO(rsorokin): get rid of the stub values. 608 // TODO(rsorokin): get rid of the stub values.
528 { 609 {
529 detail: { 610 detail: {
530 email: this.email_ || '', 611 email: this.email_ || '',
531 gaiaId: this.gaiaId_ || '', 612 gaiaId: this.gaiaId_ || '',
532 password: this.password_ || '', 613 password: this.password_ || '',
533 authCode: this.oauth_code_, 614 authCode: this.oauth_code_,
534 usingSAML: this.authFlow == AuthFlow.SAML, 615 usingSAML: this.authFlow == AuthFlow.SAML,
535 chooseWhatToSync: this.chooseWhatToSync_, 616 chooseWhatToSync: this.chooseWhatToSync_,
536 skipForNow: this.skipForNow_, 617 skipForNow: this.skipForNow_,
537 sessionIndex: this.sessionIndex_ || '', 618 sessionIndex: this.sessionIndex_ || '',
538 trusted: this.trusted_ 619 trusted: this.trusted_,
539 } 620 gapsCookie: this.new_gaps_cookie_ || this.gaps_cookie_ || '',
540 })); 621 }
622 }));
541 this.clearCredentials_(); 623 this.clearCredentials_();
542 }; 624 };
543 625
544 /** 626 /**
545 * Invoked when |samlHandler_| fires 'insecureContentBlocked' event. 627 * Invoked when |samlHandler_| fires 'insecureContentBlocked' event.
546 * @private 628 * @private
547 */ 629 */
548 Authenticator.prototype.onInsecureContentBlocked_ = function(e) { 630 Authenticator.prototype.onInsecureContentBlocked_ = function(e) {
549 if (this.insecureContentBlockedCallback) { 631 if (this.insecureContentBlockedCallback) {
550 this.insecureContentBlockedCallback(e.detail.url); 632 this.insecureContentBlockedCallback(e.detail.url);
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
682 Authenticator.AuthMode = AuthMode; 764 Authenticator.AuthMode = AuthMode;
683 Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS; 765 Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS;
684 766
685 return { 767 return {
686 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old 768 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old
687 // iframe-based flow is deprecated. 769 // iframe-based flow is deprecated.
688 GaiaAuthHost: Authenticator, 770 GaiaAuthHost: Authenticator,
689 Authenticator: Authenticator 771 Authenticator: Authenticator
690 }; 772 };
691 }); 773 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698