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'. |
dschuyler
2017/01/06 02:16:33
Could the formatter align the comments in all of S
Dan Beam
2017/01/06 02:36:50
maybe it could but there's style rules that discou
| |
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; |
dschuyler
2017/01/06 02:16:33
This looks like a typo in the original file. s/,/
Dan Beam
2017/01/06 02:36:50
yes, it is
| |
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 |