Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / gaia_auth_host / gaia_auth_host.js
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.
4
5 /**
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.
14  */
15
16 cr.define('cr.login', function() {
17   'use strict';
18
19   /**
20    * Base URL of gaia auth extension.
21    * @const
22    */
23   var AUTH_URL_BASE = 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik';
24
25   /**
26    * Auth URL to use for online flow.
27    * @const
28    */
29   var AUTH_URL = AUTH_URL_BASE + '/main.html';
30
31   /**
32    * Auth URL to use for offline flow.
33    * @const
34    */
35   var OFFLINE_AUTH_URL = AUTH_URL_BASE + '/offline.html';
36
37   /**
38    * Origin of the gaia sign in page.
39    * @const
40    */
41   var GAIA_ORIGIN = 'https://accounts.google.com';
42
43   /**
44    * Supported params of auth extension. For a complete list, check out the
45    * auth extension's main.js.
46    * @type {!Array.<string>}
47    * @const
48    */
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;
58   ];
59
60   /**
61    * Supported localized strings. For a complete list, check out the auth
62    * extension's offline.js
63    * @type {!Array.<string>}
64    * @const
65    */
66   var LOCALIZED_STRING_PARAMS = [
67       'stringSignIn',
68       'stringEmail',
69       'stringPassword',
70       'stringEmptyEmail',
71       'stringEmptyPassword',
72       'stringError'
73   ];
74
75   /**
76    * Enum for the authorization mode, must match AuthMode defined in
77    * chrome/browser/ui/webui/inline_login_ui.cc.
78    * @enum {number}
79    */
80   var AuthMode = {
81     DEFAULT: 0,
82     OFFLINE: 1,
83     DESKTOP: 2
84   };
85
86   /**
87    * Enum for the auth flow.
88    * @enum {number}
89    */
90   var AuthFlow = {
91     GAIA: 0,
92     SAML: 1
93   };
94
95   /**
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.
99    * @constructor
100    * @extends {cr.EventTarget}
101    */
102   function GaiaAuthHost(container) {
103     this.frame_ = typeof container == 'string' ? $(container) : container;
104     assert(this.frame_);
105     window.addEventListener('message',
106                             this.onMessage_.bind(this), false);
107   }
108
109   GaiaAuthHost.prototype = {
110     __proto__: cr.EventTarget.prototype,
111
112     /**
113      * An url to use with {@code reload}.
114      * @type {?string}
115      * @private
116      */
117     reloadUrl_: null,
118
119     /**
120      * The domain name of the current auth page.
121      * @type {string}
122      */
123     authDomain: '',
124
125     /**
126      * Invoked when authentication is completed successfully with credential
127      * data. A credential data object looks like this:
128      * <pre>
129      * {@code
130      * {
131      *   email: 'xx@gmail.com',
132      *   password: 'xxxx',  // May not present
133      *   authCode: 'x/xx',  // May not present
134      *   authMode: 'x',     // Authorization mode, default/offline/desktop.
135      * }
136      * }
137      * </pre>
138      * @type {function(Object)}
139      * @private
140      */
141     successCallback_: null,
142
143     /**
144      * Invoked when GAIA indicates login success and SAML was used. At this
145      * point, GAIA cookies are present but the identity of the authenticated
146      * user is not known. The embedder of GaiaAuthHost should extract the GAIA
147      * cookies from the cookie jar, query GAIA for the authenticated user's
148      * e-mail address and invoke GaiaAuthHost.setAuthenticatedUserEmail with the
149      * result. The argument is an opaque token that should be passed back to
150      * GaiaAuthHost.setAuthenticatedUserEmail.
151      * @type {function(number)}
152      */
153     retrieveAuthenticatedUserEmailCallback_: null,
154
155     /**
156      * Invoked when the auth flow needs a user to confirm his/her passwords.
157      * This could happen when there are more than one passwords scraped during
158      * SAML flow. The embedder of GaiaAuthHost should show an UI to collect a
159      * password from user then call GaiaAuthHost.verifyConfirmedPassword to
160      * verify. If the password is good, the auth flow continues with success
161      * path. Otherwise, confirmPasswordCallback_ is invoked again.
162      * @type {function()}
163      */
164     confirmPasswordCallback_: null,
165
166     /**
167      * Similar to confirmPasswordCallback_ but is used when there is no
168      * password scraped after a success authentication. The authenticated user
169      * account is passed to the callback. The embedder should take over the
170      * flow and decide what to do next.
171      * @type {function(string)}
172      */
173     noPasswordCallback_: null,
174
175     /**
176      * Invoked when the authentication flow had to be aborted because content
177      * served over an unencrypted connection was detected.
178     insecureContentBlockedCallback_: null,
179
180     /**
181      * The iframe container.
182      * @type {HTMLIFrameElement}
183      */
184     get frame() {
185       return this.frame_;
186     },
187
188     /**
189      * Sets retrieveAuthenticatedUserEmailCallback_.
190      * @type {function()}
191      */
192     set retrieveAuthenticatedUserEmailCallback(callback) {
193       this.retrieveAuthenticatedUserEmailCallback_ = callback;
194     },
195
196     /**
197      * Sets confirmPasswordCallback_.
198      * @type {function()}
199      */
200     set confirmPasswordCallback(callback) {
201       this.confirmPasswordCallback_ = callback;
202     },
203
204     /**
205      * Sets noPasswordCallback_.
206      * @type {function()}
207      */
208     set noPasswordCallback(callback) {
209       this.noPasswordCallback_ = callback;
210     },
211
212     /**
213      * Sets insecureContentBlockedCallback_.
214      * @type {function(string)}
215      */
216     set insecureContentBlockedCallback(callback) {
217       this.insecureContentBlockedCallback_ = callback;
218     },
219
220     /**
221      * Loads the auth extension.
222      * @param {AuthMode} authMode Authorization mode.
223      * @param {Object} data Parameters for the auth extension. See the auth
224      *     extension's main.js for all supported params and their defaults.
225      * @param {function(Object)} successCallback A function to be called when
226      *     the authentication is completed successfully. The callback is
227      *     invoked with a credential object.
228      */
229     load: function(authMode, data, successCallback) {
230       var params = [];
231
232       var populateParams = function(nameList, values) {
233         if (!values)
234           return;
235
236         for (var i in nameList) {
237           var name = nameList[i];
238           if (values[name])
239             params.push(name + '=' + encodeURIComponent(values[name]));
240         }
241       };
242
243       populateParams(SUPPORTED_PARAMS, data);
244       populateParams(LOCALIZED_STRING_PARAMS, data.localizedStrings);
245       params.push('parentPage=' + encodeURIComponent(window.location.origin));
246
247       var url;
248       switch (authMode) {
249         case AuthMode.OFFLINE:
250           url = OFFLINE_AUTH_URL;
251           break;
252         case AuthMode.DESKTOP:
253           url = AUTH_URL;
254           params.push('desktopMode=1');
255           break;
256         default:
257           url = AUTH_URL;
258       }
259       url += '?' + params.join('&');
260
261       this.frame_.src = url;
262       this.reloadUrl_ = url;
263       this.successCallback_ = successCallback;
264       this.authFlow = AuthFlow.GAIA;
265     },
266
267     /**
268      * Reloads the auth extension.
269      */
270     reload: function() {
271       this.frame_.src = this.reloadUrl_;
272       this.authFlow = AuthFlow.GAIA;
273     },
274
275     /**
276      * Verifies the supplied password by sending it to the auth extension,
277      * which will then check if it matches the scraped passwords.
278      * @param {string} password The confirmed password that needs verification.
279      */
280     verifyConfirmedPassword: function(password) {
281       var msg = {
282         method: 'verifyConfirmedPassword',
283         password: password
284       };
285       this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE);
286     },
287
288     /**
289      * Sends the authenticated user's e-mail address to the auth extension.
290      * @param {number} attemptToken The opaque token provided to the
291      *     retrieveAuthenticatedUserEmailCallback_.
292      * @param {string} email The authenticated user's e-mail address.
293      */
294     setAuthenticatedUserEmail: function(attemptToken, email) {
295       var msg = {
296         method: 'setAuthenticatedUserEmail',
297         attemptToken: attemptToken,
298         email: email
299       };
300       this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE);
301     },
302
303     /**
304      * Invoked to process authentication success.
305      * @param {Object} credentials Credential object to pass to success
306      *     callback.
307      * @private
308      */
309     onAuthSuccess_: function(credentials) {
310       if (this.successCallback_)
311         this.successCallback_(credentials);
312       cr.dispatchSimpleEvent(this, 'completed');
313     },
314
315     /**
316      * Checks if message comes from the loaded authentication extension.
317      * @param {Object} e Payload of the received HTML5 message.
318      * @type {boolean}
319      */
320     isAuthExtMessage_: function(e) {
321       return this.frame_.src &&
322           this.frame_.src.indexOf(e.origin) == 0 &&
323           e.source == this.frame_.contentWindow;
324     },
325
326     /**
327      * Event handler that is invoked when HTML5 message is received.
328      * @param {object} e Payload of the received HTML5 message.
329      */
330     onMessage_: function(e) {
331       var msg = e.data;
332
333       if (!this.isAuthExtMessage_(e))
334         return;
335
336       if (msg.method == 'loginUILoaded') {
337         cr.dispatchSimpleEvent(this, 'ready');
338         return;
339       }
340
341       if (/^complete(Login|Authentication)$|^offlineLogin$/.test(msg.method)) {
342         if (!msg.email && !this.email_ && !msg.skipForNow) {
343           var msg = {method: 'redirectToSignin'};
344           this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE);
345           return;
346         }
347         this.onAuthSuccess_({email: msg.email,
348                              password: msg.password,
349                              useOffline: msg.method == 'offlineLogin',
350                              usingSAML: msg.usingSAML || false,
351                              chooseWhatToSync: msg.chooseWhatToSync,
352                              skipForNow: msg.skipForNow || false,
353                              sessionIndex: msg.sessionIndex || ''});
354         return;
355       }
356
357       if (msg.method == 'retrieveAuthenticatedUserEmail') {
358         if (this.retrieveAuthenticatedUserEmailCallback_) {
359           this.retrieveAuthenticatedUserEmailCallback_(msg.attemptToken,
360                                                        msg.apiUsed);
361         } else {
362           console.error(
363               'GaiaAuthHost: Invalid retrieveAuthenticatedUserEmailCallback_.');
364         }
365         return;
366       }
367
368       if (msg.method == 'confirmPassword') {
369         if (this.confirmPasswordCallback_)
370           this.confirmPasswordCallback_(msg.passwordCount);
371         else
372           console.error('GaiaAuthHost: Invalid confirmPasswordCallback_.');
373         return;
374       }
375
376       if (msg.method == 'noPassword') {
377         if (this.noPasswordCallback_)
378           this.noPasswordCallback_(msg.email);
379         else
380           console.error('GaiaAuthHost: Invalid noPasswordCallback_.');
381         return;
382       }
383
384       if (msg.method == 'authPageLoaded') {
385         this.authDomain = msg.domain;
386         this.authFlow = msg.isSAML ? AuthFlow.SAML : AuthFlow.GAIA;
387         return;
388       }
389
390       if (msg.method == 'insecureContentBlocked') {
391         if (this.insecureContentBlockedCallback_) {
392           this.insecureContentBlockedCallback_(msg.url);
393         } else {
394           console.error(
395               'GaiaAuthHost: Invalid insecureContentBlockedCallback_.');
396         }
397         return;
398       }
399
400       if (msg.method == 'switchToFullTab') {
401         chrome.send('switchToFullTab', [msg.url]);
402         return;
403       }
404
405       console.error('Unknown message method=' + msg.method);
406     }
407   };
408
409   /**
410    * The current auth flow of the hosted gaia_auth extension.
411    * @type {AuthFlow}
412    */
413   cr.defineProperty(GaiaAuthHost, 'authFlow');
414
415   GaiaAuthHost.SUPPORTED_PARAMS = SUPPORTED_PARAMS;
416   GaiaAuthHost.LOCALIZED_STRING_PARAMS = LOCALIZED_STRING_PARAMS;
417   GaiaAuthHost.AuthMode = AuthMode;
418   GaiaAuthHost.AuthFlow = AuthFlow;
419
420   return {
421     GaiaAuthHost: GaiaAuthHost
422   };
423 });