Chromium Code Reviews| 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 /** | 5 /** |
| 6 * @fileoverview An UI component to authenciate to Chrome. The component hosts | 6 * @fileoverview An UI component to authenciate to Chrome. The component hosts |
| 7 * IdP web pages in a webview. A client who is interested in monitoring | 7 * IdP web pages in a webview. A client who is interested in monitoring |
| 8 * authentication events should pass a listener object of type | 8 * authentication events should pass a listener object of type |
| 9 * cr.login.GaiaAuthHost.Listener as defined in this file. After initialization, | 9 * cr.login.GaiaAuthHost.Listener as defined in this file. After initialization, |
| 10 * call {@code load} to start the authentication flow. | 10 * call {@code load} to start the authentication flow. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 42 */ | 42 */ |
| 43 var AuthFlow = { | 43 var AuthFlow = { |
| 44 DEFAULT: 0, | 44 DEFAULT: 0, |
| 45 SAML: 1 | 45 SAML: 1 |
| 46 }; | 46 }; |
| 47 | 47 |
| 48 /** | 48 /** |
| 49 * Initializes the authenticator component. | 49 * Initializes the authenticator component. |
| 50 * @param {webview|string} webview The webview element or its ID to host IdP | 50 * @param {webview|string} webview The webview element or its ID to host IdP |
| 51 * web pages. | 51 * web pages. |
| 52 * @param {Authenticator.Listener=} opt_listener An optional listener for | |
| 53 * authentication events. | |
| 54 * @constructor | 52 * @constructor |
| 55 * @extends {cr.EventTarget} | |
| 56 */ | 53 */ |
| 57 function Authenticator(webview, opt_listener) { | 54 function Authenticator(webview) { |
| 58 this.webview_ = typeof webview == 'string' ? $(webview) : webview; | 55 this.webview_ = typeof webview == 'string' ? $(webview) : webview; |
| 59 assert(this.webview_); | 56 assert(this.webview_); |
| 60 | 57 |
| 61 this.listener_ = opt_listener || null; | |
| 62 | |
| 63 this.email_ = null; | 58 this.email_ = null; |
| 64 this.password_ = null; | 59 this.password_ = null; |
| 65 this.gaiaId_ = null, | 60 this.gaiaId_ = null, |
| 66 this.sessionIndex_ = null; | 61 this.sessionIndex_ = null; |
| 67 this.chooseWhatToSync_ = false; | 62 this.chooseWhatToSync_ = false; |
| 68 this.skipForNow_ = false; | 63 this.skipForNow_ = false; |
| 69 this.authFlow_ = AuthFlow.DEFAULT; | 64 this.authFlow_ = AuthFlow.DEFAULT; |
| 70 this.loaded_ = false; | 65 this.loaded_ = false; |
| 71 this.idpOrigin_ = null; | 66 this.idpOrigin_ = null; |
| 72 this.continueUrl_ = null; | 67 this.continueUrl_ = null; |
| 73 this.continueUrlWithoutParams_ = null; | 68 this.continueUrlWithoutParams_ = null; |
| 74 this.initialFrameUrl_ = null; | 69 this.initialFrameUrl_ = null; |
| 75 this.reloadUrl_ = null; | 70 this.reloadUrl_ = null; |
| 71 this.trusted_ = true; | |
| 76 } | 72 } |
| 77 | 73 |
| 78 // TODO(guohui,xiyuan): no need to inherit EventTarget once we deprecate the | 74 // TODO(guohui,xiyuan): no need to inherit EventTarget once we deprecate the |
| 79 // old event-based signin flow. | 75 // old event-based signin flow. |
| 80 Authenticator.prototype = Object.create(cr.EventTarget.prototype); | 76 Authenticator.prototype = Object.create(cr.EventTarget.prototype); |
| 81 | 77 |
| 82 /** | 78 /** |
| 83 * An interface for receiving notifications upon authentication events. | |
| 84 * @interface | |
| 85 */ | |
| 86 Authenticator.Listener = function() {}; | |
| 87 | |
| 88 /** | |
| 89 * Invoked when authentication UI is ready. | |
| 90 */ | |
| 91 Authenticator.Listener.prototype.onReady = function(e) {}; | |
| 92 | |
| 93 /** | |
| 94 * Invoked when authentication is completed successfully with credential data. | |
| 95 * A credential data object looks like this: | |
| 96 * <pre> | |
| 97 * {@code | |
| 98 * { | |
| 99 * email: 'xx@gmail.com', | |
| 100 * password: 'xxxx', // May be null or empty. | |
| 101 * usingSAML: false, | |
| 102 * chooseWhatToSync: false, | |
| 103 * skipForNow: false, | |
| 104 * sessionIndex: '0' | |
| 105 * } | |
| 106 * } | |
| 107 * </pre> | |
| 108 * @param {Object} credentials A credential data object. | |
| 109 */ | |
| 110 Authenticator.Listener.prototype.onSuccess = function(credentials) {}; | |
| 111 | |
| 112 /** | |
| 113 * Invoked when the requested URL does not fit the container. | |
| 114 * @param {string} url Request URL. | |
| 115 */ | |
| 116 Authenticator.Listener.prototype.onResize = function(url) {}; | |
| 117 | |
| 118 /** | |
| 119 * Invoked when a new window event is fired. | |
| 120 * @param {Event} e Event object. | |
| 121 */ | |
| 122 Authenticator.Listener.prototype.onNewWindow = function(e) {}; | |
| 123 | |
| 124 /** | |
| 125 * Loads the authenticator component with the given parameters. | 79 * Loads the authenticator component with the given parameters. |
| 126 * @param {AuthMode} authMode Authorization mode. | 80 * @param {AuthMode} authMode Authorization mode. |
| 127 * @param {Object} data Parameters for the authorization flow. | 81 * @param {Object} data Parameters for the authorization flow. |
| 128 */ | 82 */ |
| 129 Authenticator.prototype.load = function(authMode, data) { | 83 Authenticator.prototype.load = function(authMode, data) { |
| 130 this.idpOrigin_ = data.gaiaUrl || IDP_ORIGIN; | 84 this.idpOrigin_ = data.gaiaUrl || IDP_ORIGIN; |
| 131 this.continueUrl_ = data.continueUrl || CONTINUE_URL; | 85 this.continueUrl_ = data.continueUrl || CONTINUE_URL; |
| 132 this.continueUrlWithoutParams_ = | 86 this.continueUrlWithoutParams_ = |
| 133 this.continueUrl_.substring(0, this.continueUrl_.indexOf('?')) || | 87 this.continueUrl_.substring(0, this.continueUrl_.indexOf('?')) || |
| 134 this.continueUrl_; | 88 this.continueUrl_; |
| 135 this.isConstrainedWindow_ = data.constrained == '1'; | 89 this.isConstrainedWindow_ = data.constrained == '1'; |
| 136 | 90 |
| 137 this.initialFrameUrl_ = this.constructInitialFrameUrl_(data); | 91 this.initialFrameUrl_ = this.constructInitialFrameUrl_(data); |
| 138 this.reloadUrl_ = data.frameUrl || this.initialFrameUrl_; | 92 this.reloadUrl_ = data.frameUrl || this.initialFrameUrl_; |
| 139 this.authFlow_ = AuthFlow.DEFAULT; | 93 this.authFlow_ = AuthFlow.DEFAULT; |
| 140 | 94 |
| 141 this.webview_.src = this.reloadUrl_; | 95 this.webview_.src = this.reloadUrl_; |
| 142 this.webview_.addEventListener( | 96 this.webview_.addEventListener( |
| 143 'newwindow', this.onNewWindow_.bind(this)); | 97 'newwindow', this.onNewWindow_.bind(this)); |
| 98 this.webview_.addEventListener( | |
| 99 'loadstop', this.onLoadStop_.bind(this)); | |
| 144 this.webview_.request.onCompleted.addListener( | 100 this.webview_.request.onCompleted.addListener( |
| 145 this.onRequestCompleted_.bind(this), | 101 this.onRequestCompleted_.bind(this), |
| 146 {urls: ['*://*/*', this.continueUrlWithoutParams_ + '*'], | 102 {urls: ['*://*/*', this.continueUrlWithoutParams_ + '*'], |
| 147 types: ['main_frame']}, | 103 types: ['main_frame']}, |
| 148 ['responseHeaders']); | 104 ['responseHeaders']); |
| 149 this.webview_.request.onHeadersReceived.addListener( | 105 this.webview_.request.onHeadersReceived.addListener( |
| 150 this.onHeadersReceived_.bind(this), | 106 this.onHeadersReceived_.bind(this), |
| 151 {urls: [this.idpOrigin_ + '*'], types: ['main_frame']}, | 107 {urls: [this.idpOrigin_ + '*'], types: ['main_frame']}, |
| 152 ['responseHeaders']); | 108 ['responseHeaders']); |
| 153 window.addEventListener( | 109 window.addEventListener( |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 175 url = appendParam(url, 'source', CONSTRAINED_FLOW_SOURCE); | 131 url = appendParam(url, 'source', CONSTRAINED_FLOW_SOURCE); |
| 176 return url; | 132 return url; |
| 177 }; | 133 }; |
| 178 | 134 |
| 179 /** | 135 /** |
| 180 * Invoked when a main frame request in the webview has completed. | 136 * Invoked when a main frame request in the webview has completed. |
| 181 * @private | 137 * @private |
| 182 */ | 138 */ |
| 183 Authenticator.prototype.onRequestCompleted_ = function(details) { | 139 Authenticator.prototype.onRequestCompleted_ = function(details) { |
| 184 var currentUrl = details.url; | 140 var currentUrl = details.url; |
| 141 if (currentUrl.indexOf('https') != 0) { | |
| 142 this.trusted_ = false; | |
|
noms (inactive)
2014/12/19 19:48:59
nit: no {}
Roger Tawa OOO till Jul 10th
2014/12/19 22:23:59
The convention in this file is to include { } in t
| |
| 143 } | |
| 144 | |
| 185 if (currentUrl.lastIndexOf(this.continueUrlWithoutParams_, 0) == 0) { | 145 if (currentUrl.lastIndexOf(this.continueUrlWithoutParams_, 0) == 0) { |
| 186 if (currentUrl.indexOf('ntp=1') >= 0) { | 146 if (currentUrl.indexOf('ntp=1') >= 0) { |
| 187 this.skipForNow_ = true; | 147 this.skipForNow_ = true; |
| 188 } | 148 } |
| 189 this.onAuthCompleted_(); | 149 this.onAuthCompleted_(); |
| 190 return; | 150 return; |
| 191 } | 151 } |
| 192 | 152 |
| 193 if (this.isConstrainedWindow_) { | 153 if (this.isConstrainedWindow_) { |
| 194 var isEmbeddedPage = false; | 154 var isEmbeddedPage = false; |
| 195 if (this.idpOrigin_ && currentUrl.lastIndexOf(this.idpOrigin_) == 0) { | 155 if (this.idpOrigin_ && currentUrl.lastIndexOf(this.idpOrigin_) == 0) { |
| 196 var headers = details.responseHeaders; | 156 var headers = details.responseHeaders; |
| 197 for (var i = 0; headers && i < headers.length; ++i) { | 157 for (var i = 0; headers && i < headers.length; ++i) { |
| 198 if (headers[i].name.toLowerCase() == EMBEDDED_FORM_HEADER) { | 158 if (headers[i].name.toLowerCase() == EMBEDDED_FORM_HEADER) { |
| 199 isEmbeddedPage = true; | 159 isEmbeddedPage = true; |
| 200 break; | 160 break; |
| 201 } | 161 } |
| 202 } | 162 } |
| 203 } | 163 } |
| 204 if (!isEmbeddedPage && this.listener_) { | 164 if (!isEmbeddedPage) { |
| 205 this.listener_.onResize(currentUrl); | 165 this.dispatchEvent(new CustomEvent('resize', {detail: currentUrl})); |
| 206 return; | 166 return; |
| 207 } | 167 } |
| 208 } | 168 } |
| 209 | 169 |
| 210 if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) { | 170 if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) { |
| 211 this.webview_.contentWindow.postMessage({}, currentUrl); | 171 this.webview_.contentWindow.postMessage({}, currentUrl); |
| 212 } | 172 } |
| 213 | |
| 214 if (!this.loaded_) { | |
| 215 this.loaded_ = true; | |
| 216 if (this.listener_) { | |
| 217 this.listener_.onReady(); | |
| 218 } | |
| 219 } | |
| 220 }; | 173 }; |
| 221 | 174 |
| 222 /** | 175 /** |
| 223 * Invoked when headers are received in the main frame of the webview. It | 176 * Invoked when headers are received in the main frame of the webview. It |
| 224 * 1) reads the authenticated user info from a signin header, | 177 * 1) reads the authenticated user info from a signin header, |
| 225 * 2) signals the start of a saml flow upon receiving a saml header. | 178 * 2) signals the start of a saml flow upon receiving a saml header. |
| 226 * @return {!Object} Modified request headers. | 179 * @return {!Object} Modified request headers. |
| 227 * @private | 180 * @private |
| 228 */ | 181 */ |
| 229 Authenticator.prototype.onHeadersReceived_ = function(details) { | 182 Authenticator.prototype.onHeadersReceived_ = function(details) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 270 this.password_ = msg.password; | 223 this.password_ = msg.password; |
| 271 this.chooseWhatToSync_ = msg.chooseWhatToSync; | 224 this.chooseWhatToSync_ = msg.chooseWhatToSync; |
| 272 } | 225 } |
| 273 }; | 226 }; |
| 274 | 227 |
| 275 /** | 228 /** |
| 276 * Invoked to process authentication completion. | 229 * Invoked to process authentication completion. |
| 277 * @private | 230 * @private |
| 278 */ | 231 */ |
| 279 Authenticator.prototype.onAuthCompleted_ = function() { | 232 Authenticator.prototype.onAuthCompleted_ = function() { |
| 280 if (!this.listener_) { | |
| 281 return; | |
| 282 } | |
| 283 | |
| 284 if (!this.email_ && !this.skipForNow_) { | 233 if (!this.email_ && !this.skipForNow_) { |
| 285 this.webview_.src = this.initialFrameUrl_; | 234 this.webview_.src = this.initialFrameUrl_; |
| 286 return; | 235 return; |
| 287 } | 236 } |
| 288 | 237 |
| 289 this.listener_.onSuccess({email: this.email_, | 238 this.dispatchEvent( |
| 290 gaiaId: this.gaiaId_, | 239 new CustomEvent('authCompleted', |
| 291 password: this.password_, | 240 {detail: {email: this.email_, |
| 292 usingSAML: this.authFlow_ == AuthFlow.SAML, | 241 gaiaId: this.gaiaId_, |
| 293 chooseWhatToSync: this.chooseWhatToSync_, | 242 password: this.password_, |
| 294 skipForNow: this.skipForNow_, | 243 usingSAML: this.authFlow_ == AuthFlow.SAML, |
| 295 sessionIndex: this.sessionIndex_ || ''}); | 244 chooseWhatToSync: this.chooseWhatToSync_, |
| 245 skipForNow: this.skipForNow_, | |
| 246 sessionIndex: this.sessionIndex_ || '', | |
| 247 trusted: this.trusted_}})); | |
| 296 }; | 248 }; |
| 297 | 249 |
| 298 /** | 250 /** |
| 299 * Invoked when the webview attempts to open a new window. | 251 * Invoked when the webview attempts to open a new window. |
| 300 * @private | 252 * @private |
| 301 */ | 253 */ |
| 302 Authenticator.prototype.onNewWindow_ = function(e) { | 254 Authenticator.prototype.onNewWindow_ = function(e) { |
| 303 if (!this.listener_) { | 255 this.dispatchEvent(new CustomEvent('newWindow', {detail: e})); |
| 304 return; | 256 }; |
| 257 | |
| 258 /** | |
| 259 * Invoked when the webview finishes loading a page. | |
| 260 * @private | |
| 261 */ | |
| 262 Authenticator.prototype.onLoadStop_ = function(e) { | |
| 263 if (!this.loaded_) { | |
| 264 this.loaded_ = true; | |
| 265 this.dispatchEvent(new Event('ready')); | |
| 305 } | 266 } |
| 306 | |
| 307 this.listener_.onNewWindow(e); | |
| 308 }; | 267 }; |
| 309 | 268 |
| 310 Authenticator.AuthFlow = AuthFlow; | 269 Authenticator.AuthFlow = AuthFlow; |
| 311 Authenticator.AuthMode = AuthMode; | 270 Authenticator.AuthMode = AuthMode; |
| 312 | 271 |
| 313 return { | 272 return { |
| 314 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old | 273 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old |
| 315 // iframe-based flow is deprecated. | 274 // iframe-based flow is deprecated. |
| 316 GaiaAuthHost: Authenticator | 275 GaiaAuthHost: Authenticator |
| 317 }; | 276 }; |
| 318 }); | 277 }); |
| OLD | NEW |