Update To 11.40.268.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 the auth flow needs a user to confirm his/her passwords.
145      * This could happen when there are more than one passwords scraped during
146      * SAML flow. The embedder of GaiaAuthHost should show an UI to collect a
147      * password from user then call GaiaAuthHost.verifyConfirmedPassword to
148      * verify. If the password is good, the auth flow continues with success
149      * path. Otherwise, confirmPasswordCallback_ is invoked again.
150      * @type {function()}
151      */
152     confirmPasswordCallback_: null,
153
154     /**
155      * Similar to confirmPasswordCallback_ but is used when there is no
156      * password scraped after a success authentication. The authenticated user
157      * account is passed to the callback. The embedder should take over the
158      * flow and decide what to do next.
159      * @type {function(string)}
160      */
161     noPasswordCallback_: null,
162
163     /**
164      * Invoked when the authentication flow had to be aborted because content
165      * served over an unencrypted connection was detected.
166      */
167     insecureContentBlockedCallback_: null,
168
169     /**
170      * Invoked to display an error message to the user when a GAIA error occurs
171      * during authentication.
172      * @type {function()}
173      */
174     missingGaiaInfoCallback_: null,
175
176     /**
177      * Invoked to record that the credentials passing API was used.
178      * @type {function()}
179      */
180     samlApiUsedCallback_: null,
181
182     /**
183      * The iframe container.
184      * @type {HTMLIFrameElement}
185      */
186     get frame() {
187       return this.frame_;
188     },
189
190     /**
191      * Sets confirmPasswordCallback_.
192      * @type {function()}
193      */
194     set confirmPasswordCallback(callback) {
195       this.confirmPasswordCallback_ = callback;
196     },
197
198     /**
199      * Sets noPasswordCallback_.
200      * @type {function()}
201      */
202     set noPasswordCallback(callback) {
203       this.noPasswordCallback_ = callback;
204     },
205
206     /**
207      * Sets insecureContentBlockedCallback_.
208      * @type {function(string)}
209      */
210     set insecureContentBlockedCallback(callback) {
211       this.insecureContentBlockedCallback_ = callback;
212     },
213
214     /**
215      * Sets missingGaiaInfoCallback_.
216      * @type {function()}
217      */
218     set missingGaiaInfoCallback(callback) {
219       this.missingGaiaInfoCallback_ = callback;
220     },
221
222     /**
223      * Sets samlApiUsedCallback_.
224      * @type {function()}
225      */
226     set samlApiUsedCallback(callback) {
227       this.samlApiUsedCallback_ = callback;
228     },
229
230     /**
231      * Loads the auth extension.
232      * @param {AuthMode} authMode Authorization mode.
233      * @param {Object} data Parameters for the auth extension. See the auth
234      *     extension's main.js for all supported params and their defaults.
235      * @param {function(Object)} successCallback A function to be called when
236      *     the authentication is completed successfully. The callback is
237      *     invoked with a credential object.
238      */
239     load: function(authMode, data, successCallback) {
240       var params = [];
241
242       var populateParams = function(nameList, values) {
243         if (!values)
244           return;
245
246         for (var i in nameList) {
247           var name = nameList[i];
248           if (values[name])
249             params.push(name + '=' + encodeURIComponent(values[name]));
250         }
251       };
252
253       populateParams(SUPPORTED_PARAMS, data);
254       populateParams(LOCALIZED_STRING_PARAMS, data.localizedStrings);
255       params.push('parentPage=' + encodeURIComponent(window.location.origin));
256
257       var url;
258       switch (authMode) {
259         case AuthMode.OFFLINE:
260           url = OFFLINE_AUTH_URL;
261           break;
262         case AuthMode.DESKTOP:
263           url = AUTH_URL;
264           params.push('desktopMode=1');
265           break;
266         default:
267           url = AUTH_URL;
268       }
269       url += '?' + params.join('&');
270
271       this.frame_.src = url;
272       this.reloadUrl_ = url;
273       this.successCallback_ = successCallback;
274       this.authFlow = AuthFlow.GAIA;
275     },
276
277     /**
278      * Reloads the auth extension.
279      */
280     reload: function() {
281       this.frame_.src = this.reloadUrl_;
282       this.authFlow = AuthFlow.GAIA;
283     },
284
285     /**
286      * Verifies the supplied password by sending it to the auth extension,
287      * which will then check if it matches the scraped passwords.
288      * @param {string} password The confirmed password that needs verification.
289      */
290     verifyConfirmedPassword: function(password) {
291       var msg = {
292         method: 'verifyConfirmedPassword',
293         password: password
294       };
295       this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE);
296     },
297
298     /**
299      * Invoked to process authentication success.
300      * @param {Object} credentials Credential object to pass to success
301      *     callback.
302      * @private
303      */
304     onAuthSuccess_: function(credentials) {
305       if (this.successCallback_)
306         this.successCallback_(credentials);
307       cr.dispatchSimpleEvent(this, 'completed');
308     },
309
310     /**
311      * Checks if message comes from the loaded authentication extension.
312      * @param {Object} e Payload of the received HTML5 message.
313      * @type {boolean}
314      */
315     isAuthExtMessage_: function(e) {
316       return this.frame_.src &&
317           this.frame_.src.indexOf(e.origin) == 0 &&
318           e.source == this.frame_.contentWindow;
319     },
320
321     /**
322      * Event handler that is invoked when HTML5 message is received.
323      * @param {object} e Payload of the received HTML5 message.
324      */
325     onMessage_: function(e) {
326       var msg = e.data;
327
328       if (!this.isAuthExtMessage_(e))
329         return;
330
331       if (msg.method == 'loginUILoaded') {
332         cr.dispatchSimpleEvent(this, 'ready');
333         return;
334       }
335
336       if (/^complete(Login|Authentication)$|^offlineLogin$/.test(msg.method)) {
337         if (!msg.email && !this.email_ && !msg.skipForNow) {
338           var msg = {method: 'redirectToSignin'};
339           this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE);
340           return;
341         }
342         this.onAuthSuccess_({email: msg.email,
343                              password: msg.password,
344                              gaiaId: msg.gaiaId,
345                              useOffline: msg.method == 'offlineLogin',
346                              usingSAML: msg.usingSAML || false,
347                              chooseWhatToSync: msg.chooseWhatToSync,
348                              skipForNow: msg.skipForNow || false,
349                              sessionIndex: msg.sessionIndex || ''});
350         return;
351       }
352
353       if (msg.method == 'confirmPassword') {
354         if (this.confirmPasswordCallback_)
355           this.confirmPasswordCallback_(msg.passwordCount);
356         else
357           console.error('GaiaAuthHost: Invalid confirmPasswordCallback_.');
358         return;
359       }
360
361       if (msg.method == 'noPassword') {
362         if (this.noPasswordCallback_)
363           this.noPasswordCallback_(msg.email);
364         else
365           console.error('GaiaAuthHost: Invalid noPasswordCallback_.');
366         return;
367       }
368
369       if (msg.method == 'authPageLoaded') {
370         this.authDomain = msg.domain;
371         this.authFlow = msg.isSAML ? AuthFlow.SAML : AuthFlow.GAIA;
372         return;
373       }
374
375       if (msg.method == 'insecureContentBlocked') {
376         if (this.insecureContentBlockedCallback_) {
377           this.insecureContentBlockedCallback_(msg.url);
378         } else {
379           console.error(
380               'GaiaAuthHost: Invalid insecureContentBlockedCallback_.');
381         }
382         return;
383       }
384
385       if (msg.method == 'switchToFullTab') {
386         chrome.send('switchToFullTab', [msg.url]);
387         return;
388       }
389
390       if (msg.method == 'missingGaiaInfo') {
391         if (this.missingGaiaInfoCallback_) {
392           this.missingGaiaInfoCallback_();
393         } else {
394           console.error('GaiaAuthHost: Invalid missingGaiaInfoCallback_.');
395         }
396         return;
397       }
398
399       if (msg.method == 'samlApiUsed') {
400         if (this.samlApiUsedCallback_) {
401           this.samlApiUsedCallback_();
402         } else {
403           console.error('GaiaAuthHost: Invalid samlApiUsedCallback_.');
404         }
405         return;
406       }
407
408       console.error('Unknown message method=' + msg.method);
409     }
410   };
411
412   /**
413    * The current auth flow of the hosted gaia_auth extension.
414    * @type {AuthFlow}
415    */
416   cr.defineProperty(GaiaAuthHost, 'authFlow');
417
418   GaiaAuthHost.SUPPORTED_PARAMS = SUPPORTED_PARAMS;
419   GaiaAuthHost.LOCALIZED_STRING_PARAMS = LOCALIZED_STRING_PARAMS;
420   GaiaAuthHost.AuthMode = AuthMode;
421   GaiaAuthHost.AuthFlow = AuthFlow;
422
423   return {
424     GaiaAuthHost: GaiaAuthHost
425   };
426 });