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