1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * @fileoverview An UI component to host gaia auth extension in an iframe.
7 * After the component binds with an iframe, call its {@code load} to start the
8 * authentication flow. There are two events would be raised after this point:
9 * a 'ready' event when the authentication UI is ready to use and a 'completed'
10 * event when the authentication is completed successfully. If caller is
11 * interested in the user credentials, he may supply a success callback with
12 * {@code load} call. The callback will be invoked when the authentication is
13 * completed successfully and with the available credential data.
16 cr
.define('cr.login', function() {
20 * Base URL of gaia auth extension.
23 var AUTH_URL_BASE
= 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik';
26 * Auth URL to use for online flow.
29 var AUTH_URL
= AUTH_URL_BASE
+ '/main.html';
32 * Auth URL to use for offline flow.
35 var OFFLINE_AUTH_URL
= AUTH_URL_BASE
+ '/offline.html';
38 * Origin of the gaia sign in page.
41 var GAIA_ORIGIN
= 'https://accounts.google.com';
44 * Supported params of auth extension. For a complete list, check out the
45 * auth extension's main.js.
46 * @type {!Array<string>}
49 var SUPPORTED_PARAMS
= [
50 'gaiaUrl', // Gaia url to use;
51 'gaiaPath', // Gaia path to use without a leading slash;
52 'hl', // Language code for the user interface;
53 'email', // Pre-fill the email field in Gaia UI;
54 'service', // Name of Gaia service;
55 'continueUrl', // Continue url to use;
56 'frameUrl', // Initial frame URL to use. If empty defaults to gaiaUrl.
57 'constrained' // Whether the extension is loaded in a constrained window;
61 * Supported localized strings. For a complete list, check out the auth
62 * extension's offline.js
63 * @type {!Array<string>}
66 var LOCALIZED_STRING_PARAMS
= [
71 'stringEmptyPassword',
76 * Enum for the authorization mode, must match AuthMode defined in
77 * chrome/browser/ui/webui/inline_login_ui.cc.
87 * Enum for the auth flow.
96 * Creates a new gaia auth extension host.
97 * @param {HTMLIFrameElement|string} container The iframe element or its id
98 * to host the auth extension.
100 * @extends {cr.EventTarget}
102 function GaiaAuthHost(container
) {
103 this.frame_
= typeof container
== 'string' ? $(container
) : container
;
105 window
.addEventListener('message',
106 this.onMessage_
.bind(this), false);
109 GaiaAuthHost
.prototype = {
110 __proto__
: cr
.EventTarget
.prototype,
113 * Auth extension params
119 * An url to use with {@code reload}.
126 * The domain name of the current auth page.
132 * Invoked when authentication is completed successfully with credential
133 * data. A credential data object looks like this:
137 * email: 'xx@gmail.com',
138 * password: 'xxxx', // May not present
139 * authCode: 'x/xx', // May not present
140 * authMode: 'x', // Authorization mode, default/offline/desktop.
144 * @type {function(Object)}
147 successCallback_
: null,
150 * Invoked when the auth flow needs a user to confirm his/her passwords.
151 * This could happen when there are more than one passwords scraped during
152 * SAML flow. The embedder of GaiaAuthHost should show an UI to collect a
153 * password from user then call GaiaAuthHost.verifyConfirmedPassword to
154 * verify. If the password is good, the auth flow continues with success
155 * path. Otherwise, confirmPasswordCallback_ is invoked again.
158 confirmPasswordCallback_
: null,
161 * Similar to confirmPasswordCallback_ but is used when there is no
162 * password scraped after a success authentication. The authenticated user
163 * account is passed to the callback. The embedder should take over the
164 * flow and decide what to do next.
165 * @type {function(string)}
167 noPasswordCallback_
: null,
170 * Invoked when the authentication flow had to be aborted because content
171 * served over an unencrypted connection was detected.
173 insecureContentBlockedCallback_
: null,
176 * Invoked to display an error message to the user when a GAIA error occurs
177 * during authentication.
180 missingGaiaInfoCallback_
: null,
183 * Invoked to record that the credentials passing API was used.
186 samlApiUsedCallback_
: null,
189 * The iframe container.
190 * @type {HTMLIFrameElement}
197 * Sets confirmPasswordCallback_.
200 set confirmPasswordCallback(callback
) {
201 this.confirmPasswordCallback_
= callback
;
205 * Sets noPasswordCallback_.
208 set noPasswordCallback(callback
) {
209 this.noPasswordCallback_
= callback
;
213 * Sets insecureContentBlockedCallback_.
214 * @type {function(string)}
216 set insecureContentBlockedCallback(callback
) {
217 this.insecureContentBlockedCallback_
= callback
;
221 * Sets missingGaiaInfoCallback_.
224 set missingGaiaInfoCallback(callback
) {
225 this.missingGaiaInfoCallback_
= callback
;
229 * Sets samlApiUsedCallback_.
232 set samlApiUsedCallback(callback
) {
233 this.samlApiUsedCallback_
= callback
;
237 * Loads the auth extension.
238 * @param {AuthMode} authMode Authorization mode.
239 * @param {Object} data Parameters for the auth extension. See the auth
240 * extension's main.js for all supported params and their defaults.
241 * @param {function(Object)} successCallback A function to be called when
242 * the authentication is completed successfully. The callback is
243 * invoked with a credential object.
245 load: function(authMode
, data
, successCallback
) {
248 var populateParams = function(nameList
, values
) {
252 for (var i
in nameList
) {
253 var name
= nameList
[i
];
255 params
[name
] = values
[name
];
259 populateParams(SUPPORTED_PARAMS
, data
);
260 populateParams(LOCALIZED_STRING_PARAMS
, data
.localizedStrings
);
261 params
['needPassword'] = true;
265 case AuthMode
.OFFLINE
:
266 url
= OFFLINE_AUTH_URL
;
268 case AuthMode
.DESKTOP
:
270 params
['desktopMode'] = true;
276 this.authParams_
= params
;
277 this.reloadUrl_
= url
;
278 this.successCallback_
= successCallback
;
284 * Reloads the auth extension.
287 var sendParamsOnLoad = function() {
288 this.frame_
.removeEventListener('load', sendParamsOnLoad
);
289 this.frame_
.contentWindow
.postMessage(this.authParams_
, AUTH_URL_BASE
);
292 this.frame_
.addEventListener('load', sendParamsOnLoad
);
293 this.frame_
.src
= this.reloadUrl_
;
294 this.authFlow
= AuthFlow
.GAIA
;
298 * Verifies the supplied password by sending it to the auth extension,
299 * which will then check if it matches the scraped passwords.
300 * @param {string} password The confirmed password that needs verification.
302 verifyConfirmedPassword: function(password
) {
304 method
: 'verifyConfirmedPassword',
307 this.frame_
.contentWindow
.postMessage(msg
, AUTH_URL_BASE
);
311 * Invoked to process authentication success.
312 * @param {Object} credentials Credential object to pass to success
316 onAuthSuccess_: function(credentials
) {
317 if (this.successCallback_
)
318 this.successCallback_(credentials
);
319 cr
.dispatchSimpleEvent(this, 'completed');
323 * Checks if message comes from the loaded authentication extension.
324 * @param {Object} e Payload of the received HTML5 message.
327 isAuthExtMessage_: function(e
) {
328 return this.frame_
.src
&&
329 this.frame_
.src
.indexOf(e
.origin
) == 0 &&
330 e
.source
== this.frame_
.contentWindow
;
334 * Event handler that is invoked when HTML5 message is received.
335 * @param {object} e Payload of the received HTML5 message.
337 onMessage_: function(e
) {
340 if (!this.isAuthExtMessage_(e
))
343 if (msg
.method
== 'loginUILoaded') {
344 cr
.dispatchSimpleEvent(this, 'ready');
348 if (/^complete(Login|Authentication)$|^offlineLogin$/.test(msg
.method
)) {
349 if (!msg
.email
&& !this.email_
&& !msg
.skipForNow
) {
350 var msg
= {method
: 'redirectToSignin'};
351 this.frame_
.contentWindow
.postMessage(msg
, AUTH_URL_BASE
);
354 this.onAuthSuccess_({email
: msg
.email
,
355 password
: msg
.password
,
357 useOffline
: msg
.method
== 'offlineLogin',
358 usingSAML
: msg
.usingSAML
|| false,
359 chooseWhatToSync
: msg
.chooseWhatToSync
,
360 skipForNow
: msg
.skipForNow
|| false,
361 sessionIndex
: msg
.sessionIndex
|| ''});
365 if (msg
.method
== 'confirmPassword') {
366 if (this.confirmPasswordCallback_
)
367 this.confirmPasswordCallback_(msg
.passwordCount
);
369 console
.error('GaiaAuthHost: Invalid confirmPasswordCallback_.');
373 if (msg
.method
== 'noPassword') {
374 if (this.noPasswordCallback_
)
375 this.noPasswordCallback_(msg
.email
);
377 console
.error('GaiaAuthHost: Invalid noPasswordCallback_.');
381 if (msg
.method
== 'authPageLoaded') {
382 this.authDomain
= msg
.domain
;
383 this.authFlow
= msg
.isSAML
? AuthFlow
.SAML
: AuthFlow
.GAIA
;
387 if (msg
.method
== 'resetAuthFlow') {
388 this.authFlow
= AuthFlow
.GAIA
;
392 if (msg
.method
== 'insecureContentBlocked') {
393 if (this.insecureContentBlockedCallback_
) {
394 this.insecureContentBlockedCallback_(msg
.url
);
397 'GaiaAuthHost: Invalid insecureContentBlockedCallback_.');
402 if (msg
.method
== 'switchToFullTab') {
403 chrome
.send('switchToFullTab', [msg
.url
]);
407 if (msg
.method
== 'missingGaiaInfo') {
408 if (this.missingGaiaInfoCallback_
) {
409 this.missingGaiaInfoCallback_();
411 console
.error('GaiaAuthHost: Invalid missingGaiaInfoCallback_.');
416 if (msg
.method
== 'samlApiUsed') {
417 if (this.samlApiUsedCallback_
) {
418 this.samlApiUsedCallback_();
420 console
.error('GaiaAuthHost: Invalid samlApiUsedCallback_.');
425 console
.error('Unknown message method=' + msg
.method
);
430 * The current auth flow of the hosted gaia_auth extension.
433 cr
.defineProperty(GaiaAuthHost
, 'authFlow');
435 GaiaAuthHost
.SUPPORTED_PARAMS
= SUPPORTED_PARAMS
;
436 GaiaAuthHost
.LOCALIZED_STRING_PARAMS
= LOCALIZED_STRING_PARAMS
;
437 GaiaAuthHost
.AuthMode
= AuthMode
;
438 GaiaAuthHost
.AuthFlow
= AuthFlow
;
441 GaiaAuthHost
: GaiaAuthHost