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

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

Issue 807503004: While trying to enable webview sign-in by default, I found a bunch of issues (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add comment Created 5 years, 11 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 /** 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.
11 */ 11 */
12 cr.define('cr.login', function() { 12 cr.define('cr.login', function() {
13 'use strict'; 13 'use strict';
14 14
15 // TODO(rogerta): should use gaia URL from GaiaUrls::gaia_url() instead
16 // of hardcoding the prod URL here. As is, this does not work with staging
17 // environments.
15 var IDP_ORIGIN = 'https://accounts.google.com/'; 18 var IDP_ORIGIN = 'https://accounts.google.com/';
16 var IDP_PATH = 'ServiceLogin?skipvpage=true&sarp=1&rm=hide'; 19 var IDP_PATH = 'ServiceLogin?skipvpage=true&sarp=1&rm=hide';
17 var CONTINUE_URL = 20 var CONTINUE_URL =
18 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/success.html'; 21 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/success.html';
19 var SIGN_IN_HEADER = 'google-accounts-signin'; 22 var SIGN_IN_HEADER = 'google-accounts-signin';
20 var EMBEDDED_FORM_HEADER = 'google-accounts-embedded'; 23 var EMBEDDED_FORM_HEADER = 'google-accounts-embedded';
21 var SAML_HEADER = 'google-accounts-saml'; 24 var SAML_HEADER = 'google-accounts-saml';
22 25
23 /** 26 /**
24 * The source URL parameter for the constrained signin flow. 27 * The source URL parameter for the constrained signin flow.
(...skipping 17 matching lines...) Expand all
42 */ 45 */
43 var AuthFlow = { 46 var AuthFlow = {
44 DEFAULT: 0, 47 DEFAULT: 0,
45 SAML: 1 48 SAML: 1
46 }; 49 };
47 50
48 /** 51 /**
49 * Initializes the authenticator component. 52 * Initializes the authenticator component.
50 * @param {webview|string} webview The webview element or its ID to host IdP 53 * @param {webview|string} webview The webview element or its ID to host IdP
51 * web pages. 54 * web pages.
52 * @param {Authenticator.Listener=} opt_listener An optional listener for
53 * authentication events.
54 * @constructor 55 * @constructor
55 * @extends {cr.EventTarget}
56 */ 56 */
57 function Authenticator(webview, opt_listener) { 57 function Authenticator(webview) {
58 this.webview_ = typeof webview == 'string' ? $(webview) : webview; 58 this.webview_ = typeof webview == 'string' ? $(webview) : webview;
59 assert(this.webview_); 59 assert(this.webview_);
60 60
61 this.listener_ = opt_listener || null;
62
63 this.email_ = null; 61 this.email_ = null;
64 this.password_ = null; 62 this.password_ = null;
65 this.gaiaId_ = null, 63 this.gaiaId_ = null,
66 this.sessionIndex_ = null; 64 this.sessionIndex_ = null;
67 this.chooseWhatToSync_ = false; 65 this.chooseWhatToSync_ = false;
68 this.skipForNow_ = false; 66 this.skipForNow_ = false;
69 this.authFlow_ = AuthFlow.DEFAULT; 67 this.authFlow_ = AuthFlow.DEFAULT;
70 this.loaded_ = false; 68 this.loaded_ = false;
71 this.idpOrigin_ = null; 69 this.idpOrigin_ = null;
72 this.continueUrl_ = null; 70 this.continueUrl_ = null;
73 this.continueUrlWithoutParams_ = null; 71 this.continueUrlWithoutParams_ = null;
74 this.initialFrameUrl_ = null; 72 this.initialFrameUrl_ = null;
75 this.reloadUrl_ = null; 73 this.reloadUrl_ = null;
74 this.trusted_ = true;
76 } 75 }
77 76
78 // TODO(guohui,xiyuan): no need to inherit EventTarget once we deprecate the 77 // TODO(guohui,xiyuan): no need to inherit EventTarget once we deprecate the
79 // old event-based signin flow. 78 // old event-based signin flow.
80 Authenticator.prototype = Object.create(cr.EventTarget.prototype); 79 Authenticator.prototype = Object.create(cr.EventTarget.prototype);
81 80
82 /** 81 /**
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. 82 * Loads the authenticator component with the given parameters.
126 * @param {AuthMode} authMode Authorization mode. 83 * @param {AuthMode} authMode Authorization mode.
127 * @param {Object} data Parameters for the authorization flow. 84 * @param {Object} data Parameters for the authorization flow.
128 */ 85 */
129 Authenticator.prototype.load = function(authMode, data) { 86 Authenticator.prototype.load = function(authMode, data) {
130 this.idpOrigin_ = data.gaiaUrl || IDP_ORIGIN; 87 this.idpOrigin_ = data.gaiaUrl || IDP_ORIGIN;
131 this.continueUrl_ = data.continueUrl || CONTINUE_URL; 88 this.continueUrl_ = data.continueUrl || CONTINUE_URL;
132 this.continueUrlWithoutParams_ = 89 this.continueUrlWithoutParams_ =
133 this.continueUrl_.substring(0, this.continueUrl_.indexOf('?')) || 90 this.continueUrl_.substring(0, this.continueUrl_.indexOf('?')) ||
134 this.continueUrl_; 91 this.continueUrl_;
135 this.isConstrainedWindow_ = data.constrained == '1'; 92 this.isConstrainedWindow_ = data.constrained == '1';
136 93
137 this.initialFrameUrl_ = this.constructInitialFrameUrl_(data); 94 this.initialFrameUrl_ = this.constructInitialFrameUrl_(data);
138 this.reloadUrl_ = data.frameUrl || this.initialFrameUrl_; 95 this.reloadUrl_ = data.frameUrl || this.initialFrameUrl_;
139 this.authFlow_ = AuthFlow.DEFAULT; 96 this.authFlow_ = AuthFlow.DEFAULT;
140 97
141 this.webview_.src = this.reloadUrl_; 98 this.webview_.src = this.reloadUrl_;
142 this.webview_.addEventListener( 99 this.webview_.addEventListener(
143 'newwindow', this.onNewWindow_.bind(this)); 100 'newwindow', this.onNewWindow_.bind(this));
101 this.webview_.addEventListener(
102 'loadstop', this.onLoadStop_.bind(this));
144 this.webview_.request.onCompleted.addListener( 103 this.webview_.request.onCompleted.addListener(
145 this.onRequestCompleted_.bind(this), 104 this.onRequestCompleted_.bind(this),
146 {urls: ['*://*/*', this.continueUrlWithoutParams_ + '*'], 105 {urls: ['*://*/*', this.continueUrlWithoutParams_ + '*'],
147 types: ['main_frame']}, 106 types: ['main_frame']},
148 ['responseHeaders']); 107 ['responseHeaders']);
149 this.webview_.request.onHeadersReceived.addListener( 108 this.webview_.request.onHeadersReceived.addListener(
150 this.onHeadersReceived_.bind(this), 109 this.onHeadersReceived_.bind(this),
151 {urls: [this.idpOrigin_ + '*'], types: ['main_frame']}, 110 {urls: [this.idpOrigin_ + '*'], types: ['main_frame']},
152 ['responseHeaders']); 111 ['responseHeaders']);
153 window.addEventListener( 112 window.addEventListener(
154 'message', this.onMessage_.bind(this), false); 113 'message', this.onMessageFromWebview_.bind(this), false);
155 }; 114 };
156 115
157 /** 116 /**
158 * Reloads the authenticator component. 117 * Reloads the authenticator component.
159 */ 118 */
160 Authenticator.prototype.reload = function() { 119 Authenticator.prototype.reload = function() {
161 this.webview_.src = this.reloadUrl_; 120 this.webview_.src = this.reloadUrl_;
162 this.authFlow_ = AuthFlow.DEFAULT; 121 this.authFlow_ = AuthFlow.DEFAULT;
163 }; 122 };
164 123
(...skipping 10 matching lines...) Expand all
175 url = appendParam(url, 'source', CONSTRAINED_FLOW_SOURCE); 134 url = appendParam(url, 'source', CONSTRAINED_FLOW_SOURCE);
176 return url; 135 return url;
177 }; 136 };
178 137
179 /** 138 /**
180 * Invoked when a main frame request in the webview has completed. 139 * Invoked when a main frame request in the webview has completed.
181 * @private 140 * @private
182 */ 141 */
183 Authenticator.prototype.onRequestCompleted_ = function(details) { 142 Authenticator.prototype.onRequestCompleted_ = function(details) {
184 var currentUrl = details.url; 143 var currentUrl = details.url;
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
153 if (currentUrl.indexOf('https') != 0) {
154 this.trusted_ = false;
155 }
156
193 if (this.isConstrainedWindow_) { 157 if (this.isConstrainedWindow_) {
194 var isEmbeddedPage = false; 158 var isEmbeddedPage = false;
195 if (this.idpOrigin_ && currentUrl.lastIndexOf(this.idpOrigin_) == 0) { 159 if (this.idpOrigin_ && currentUrl.lastIndexOf(this.idpOrigin_) == 0) {
196 var headers = details.responseHeaders; 160 var headers = details.responseHeaders;
197 for (var i = 0; headers && i < headers.length; ++i) { 161 for (var i = 0; headers && i < headers.length; ++i) {
198 if (headers[i].name.toLowerCase() == EMBEDDED_FORM_HEADER) { 162 if (headers[i].name.toLowerCase() == EMBEDDED_FORM_HEADER) {
199 isEmbeddedPage = true; 163 isEmbeddedPage = true;
200 break; 164 break;
201 } 165 }
202 } 166 }
203 } 167 }
204 if (!isEmbeddedPage && this.listener_) { 168 if (!isEmbeddedPage) {
205 this.listener_.onResize(currentUrl); 169 this.dispatchEvent(new CustomEvent('resize', {detail: currentUrl}));
206 return; 170 return;
207 } 171 }
208 } 172 }
209 173
210 if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) { 174 if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) {
211 this.webview_.contentWindow.postMessage({}, currentUrl); 175 this.webview_.contentWindow.postMessage({}, currentUrl);
212 } 176 }
213
214 if (!this.loaded_) {
215 this.loaded_ = true;
216 if (this.listener_) {
217 this.listener_.onReady();
218 }
219 }
220 }; 177 };
221 178
222 /** 179 /**
223 * Invoked when headers are received in the main frame of the webview. It 180 * Invoked when headers are received in the main frame of the webview. It
224 * 1) reads the authenticated user info from a signin header, 181 * 1) reads the authenticated user info from a signin header,
225 * 2) signals the start of a saml flow upon receiving a saml header. 182 * 2) signals the start of a saml flow upon receiving a saml header.
226 * @return {!Object} Modified request headers. 183 * @return {!Object} Modified request headers.
227 * @private 184 * @private
228 */ 185 */
229 Authenticator.prototype.onHeadersReceived_ = function(details) { 186 Authenticator.prototype.onHeadersReceived_ = function(details) {
(...skipping 17 matching lines...) Expand all
247 } 204 }
248 this.gaiaId_ = signinDetails['obfuscatedid'].slice(1, -1); 205 this.gaiaId_ = signinDetails['obfuscatedid'].slice(1, -1);
249 this.sessionIndex_ = signinDetails['sessionindex']; 206 this.sessionIndex_ = signinDetails['sessionindex'];
250 } else if (headerName == SAML_HEADER) { 207 } else if (headerName == SAML_HEADER) {
251 this.authFlow_ = AuthFlow.SAML; 208 this.authFlow_ = AuthFlow.SAML;
252 } 209 }
253 } 210 }
254 }; 211 };
255 212
256 /** 213 /**
257 * Invoked when an HTML5 message is received. 214 * Invoked when an HTML5 message is received from the webview element.
258 * @param {object} e Payload of the received HTML5 message. 215 * @param {object} e Payload of the received HTML5 message.
259 * @private 216 * @private
260 */ 217 */
261 Authenticator.prototype.onMessage_ = function(e) { 218 Authenticator.prototype.onMessageFromWebview_ = function(e) {
262 if (e.origin != this.idpOrigin_) { 219 // The event origin does not have a trailing slash.
220 if (e.origin != this.idpOrigin_.substring(0, this.idpOrigin_ - 1)) {
263 return; 221 return;
264 } 222 }
265 223
266 var msg = e.data; 224 var msg = e.data;
267
268 if (msg.method == 'attemptLogin') { 225 if (msg.method == 'attemptLogin') {
269 this.email_ = msg.email; 226 this.email_ = msg.email;
270 this.password_ = msg.password; 227 this.password_ = msg.password;
271 this.chooseWhatToSync_ = msg.chooseWhatToSync; 228 this.chooseWhatToSync_ = msg.chooseWhatToSync;
272 } 229 }
273 }; 230 };
274 231
275 /** 232 /**
276 * Invoked to process authentication completion. 233 * Invoked to process authentication completion.
277 * @private 234 * @private
278 */ 235 */
279 Authenticator.prototype.onAuthCompleted_ = function() { 236 Authenticator.prototype.onAuthCompleted_ = function() {
280 if (!this.listener_) {
281 return;
282 }
283
284 if (!this.email_ && !this.skipForNow_) { 237 if (!this.email_ && !this.skipForNow_) {
285 this.webview_.src = this.initialFrameUrl_; 238 this.webview_.src = this.initialFrameUrl_;
286 return; 239 return;
287 } 240 }
288 241
289 this.listener_.onSuccess({email: this.email_, 242 this.dispatchEvent(
290 gaiaId: this.gaiaId_, 243 new CustomEvent('authCompleted',
291 password: this.password_, 244 {detail: {email: this.email_,
292 usingSAML: this.authFlow_ == AuthFlow.SAML, 245 gaiaId: this.gaiaId_,
293 chooseWhatToSync: this.chooseWhatToSync_, 246 password: this.password_,
294 skipForNow: this.skipForNow_, 247 usingSAML: this.authFlow_ == AuthFlow.SAML,
295 sessionIndex: this.sessionIndex_ || ''}); 248 chooseWhatToSync: this.chooseWhatToSync_,
249 skipForNow: this.skipForNow_,
250 sessionIndex: this.sessionIndex_ || '',
251 trusted: this.trusted_}}));
296 }; 252 };
297 253
298 /** 254 /**
299 * Invoked when the webview attempts to open a new window. 255 * Invoked when the webview attempts to open a new window.
300 * @private 256 * @private
301 */ 257 */
302 Authenticator.prototype.onNewWindow_ = function(e) { 258 Authenticator.prototype.onNewWindow_ = function(e) {
303 if (!this.listener_) { 259 this.dispatchEvent(new CustomEvent('newWindow', {detail: e}));
304 return; 260 };
261
262 /**
263 * Invoked when the webview finishes loading a page.
264 * @private
265 */
266 Authenticator.prototype.onLoadStop_ = function(e) {
267 if (!this.loaded_) {
268 this.loaded_ = true;
269 this.dispatchEvent(new Event('ready'));
305 } 270 }
306
307 this.listener_.onNewWindow(e);
308 }; 271 };
309 272
310 Authenticator.AuthFlow = AuthFlow; 273 Authenticator.AuthFlow = AuthFlow;
311 Authenticator.AuthMode = AuthMode; 274 Authenticator.AuthMode = AuthMode;
312 275
313 return { 276 return {
314 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old 277 // TODO(guohui, xiyuan): Rename GaiaAuthHost to Authenticator once the old
315 // iframe-based flow is deprecated. 278 // iframe-based flow is deprecated.
316 GaiaAuthHost: Authenticator 279 GaiaAuthHost: Authenticator
317 }; 280 };
318 }); 281 });
OLDNEW
« no previous file with comments | « chrome/browser/profiles/host_zoom_map_browsertest.cc ('k') | chrome/browser/resources/inline_login/inline_login.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698