cros: Remove default pinned apps trial.
[chromium-blink-merge.git] / chrome / browser / resources / gaia_auth_host / gaia_auth_host.js
blob9aff4bacf4e2aacd66a1f1156fd9d5d01da09607
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.
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  */
16 cr.define('cr.login', function() {
17   'use strict';
19   /**
20    * Base URL of gaia auth extension.
21    * @const
22    */
23   var AUTH_URL_BASE = 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik';
25   /**
26    * Auth URL to use for online flow.
27    * @const
28    */
29   var AUTH_URL = AUTH_URL_BASE + '/main.html';
31   /**
32    * Auth URL to use for offline flow.
33    * @const
34    */
35   var OFFLINE_AUTH_URL = AUTH_URL_BASE + '/offline.html';
37   /**
38    * Auth URL to use for inline flow.
39    * @const
40    */
41   var INLINE_AUTH_URL = AUTH_URL_BASE + '/inline_main.html';
43   /**
44    * Origin of the gaia sign in page.
45    * @const
46    */
47   var GAIA_ORIGIN = 'https://accounts.google.com';
49   /**
50    * Supported params of auth extension. For a complete list, check out the
51    * auth extension's main.js.
52    * @type {!Array.<string>}
53    * @const
54    */
55   var SUPPORTED_PARAMS = [
56     'gaiaUrl',       // Gaia url to use;
57     'gaiaPath',      // Gaia path to use without a leading slash;
58     'hl',            // Language code for the user interface;
59     'email',         // Pre-fill the email field in Gaia UI;
60     'service',       // Name of Gaia service;
61     'continueUrl',   // Continue url to use;
62     'partitionId',   // Partition ID for the embedded Gaia webview;
63     'frameUrl',      // Initial frame URL to use. If empty defaults to gaiaUrl.
64     'constrained'    // Whether the extension is loaded in a constrained window;
65   ];
67   /**
68    * Supported localized strings. For a complete list, check out the auth
69    * extension's offline.js
70    * @type {!Array.<string>}
71    * @const
72    */
73   var LOCALIZED_STRING_PARAMS = [
74       'stringSignIn',
75       'stringEmail',
76       'stringPassword',
77       'stringEmptyEmail',
78       'stringEmptyPassword',
79       'stringError'
80   ];
82   /**
83    * Enum for the authorization mode, must match AuthMode defined in
84    * chrome/browser/ui/webui/inline_login_ui.cc.
85    * @enum {number}
86    */
87   var AuthMode = {
88     DEFAULT: 0,
89     OFFLINE: 1,
90     INLINE: 2
91   };
93   /**
94    * Creates a new gaia auth extension host.
95    * @param {HTMLIFrameElement|string} container The iframe element or its id
96    *     to host the auth extension.
97    * @constructor
98    * @extends {cr.EventTarget}
99    */
100   function GaiaAuthHost(container) {
101     this.frame_ = typeof container == 'string' ? $(container) : container;
102     assert(this.frame_);
103     window.addEventListener('message',
104                             this.onMessage_.bind(this), false);
105     window.addEventListener('popstate',
106                             this.onPopState_.bind(this), false);
107   }
109   GaiaAuthHost.prototype = {
110     __proto__: cr.EventTarget.prototype,
112     /**
113      * An url to use with {@code reload}.
114      * @type {?string}
115      * @private
116      */
117     reloadUrl_: null,
119     /**
120      * Invoked when authentication is completed successfully with credential
121      * data. A credential data object looks like this:
122      * <pre>
123      * {@code
124      * {
125      *   email: 'xx@gmail.com',
126      *   password: 'xxxx',  // May not present
127      *   authCode: 'x/xx',  // May not present
128      *   authMode: 'x',     // Authorization mode, default/inline/offline.
129      * }
130      * }
131      * </pre>
132      * @type {function(Object)}
133      * @private
134      */
135     successCallback_: null,
137     /**
138      * Invoked when the auth flow needs a user to confirm his/her passwords.
139      * This could happen when there are more than one passwords scraped during
140      * SAML flow. The embedder of GaiaAuthHost should show an UI to collect a
141      * password from user then call GaiaAuthHost.verifyConfirmedPassword to
142      * verify. If the password is good, the auth flow continues with success
143      * path. Otherwise, confirmPasswordCallback_ is invoked again.
144      * @type {function()}
145      */
146     confirmPasswordCallback_: null,
148     /**
149      * Similar to confirmPasswordCallback_ but is used when there is no
150      * password scraped after a success authentication. The authenticated user
151      * account is passed to the callback. The embedder should take over the
152      * flow and decide what to do next.
153      * @type {function(string)}
154      */
155     noPasswordCallback_: null,
157     /**
158      * Invoked when the auth page hosted inside the extension is loaded.
159      * Param {@code saml} is true when the auth page is a SAML page (out of
160      * Gaia domain.
161      * @type {function{boolean)}
162      */
163     authPageLoadedCallback_: null,
165     /**
166      * The iframe container.
167      * @type {HTMLIFrameElement}
168      */
169     get frame() {
170       return this.frame_;
171     },
173     /**
174      * Sets confirmPasswordCallback_.
175      * @type {function()}
176      */
177     set confirmPasswordCallback(callback) {
178       this.confirmPasswordCallback_ = callback;
179     },
181     /**
182      * Sets noPasswordCallback_.
183      * @type {function()}
184      */
185     set noPasswordCallback(callback) {
186       this.noPasswordCallback_ = callback;
187     },
189     /**
190      * Sets authPageLoadedCallback_.
191      * @type {function(boolean)}
192      */
193     set authPageLoadedCallback(callback) {
194       this.authPageLoadedCallback_ = callback;
195     },
197     /**
198      * Loads the auth extension.
199      * @param {AuthMode} authMode Authorization mode.
200      * @param {Object} data Parameters for the auth extension. See the auth
201      *     extension's main.js for all supported params and their defaults.
202      * @param {function(Object)} successCallback A function to be called when
203      *     the authentication is completed successfully. The callback is
204      *     invoked with a credential object.
205      */
206     load: function(authMode, data, successCallback) {
207       var params = [];
209       var populateParams = function(nameList, values) {
210         if (!values)
211           return;
213         for (var i in nameList) {
214           var name = nameList[i];
215           if (values[name])
216             params.push(name + '=' + encodeURIComponent(values[name]));
217         }
218       };
220       populateParams(SUPPORTED_PARAMS, data);
221       populateParams(LOCALIZED_STRING_PARAMS, data.localizedStrings);
222       params.push('parentPage=' + encodeURIComponent(window.location.origin));
224       var url;
225       switch (authMode) {
226         case AuthMode.OFFLINE:
227           url = OFFLINE_AUTH_URL;
228           break;
229         case AuthMode.INLINE:
230           url = INLINE_AUTH_URL;
231           params.push('inlineMode=1');
232           break;
233         default:
234           url = AUTH_URL;
235       }
236       url += '?' + params.join('&');
238       this.frame_.src = url;
239       this.reloadUrl_ = url;
240       this.successCallback_ = successCallback;
241     },
243     /**
244      * Reloads the auth extension.
245      */
246     reload: function() {
247       this.frame_.src = this.reloadUrl_;
248     },
250     /**
251      * Verifies the supplied password by sending it to the auth extension,
252      * which will then check if it matches the scraped passwords.
253      * @param {string} password The confirmed password that needs verification.
254      */
255     verifyConfirmedPassword: function(password) {
256       var msg = {
257         method: 'verifyConfirmedPassword',
258         password: password
259       };
260       this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE);
261     },
263     /**
264      * Invoked to process authentication success.
265      * @param {Object} credentials Credential object to pass to success
266      *     callback.
267      * @private
268      */
269     onAuthSuccess_: function(credentials) {
270       if (this.successCallback_)
271         this.successCallback_(credentials);
272       cr.dispatchSimpleEvent(this, 'completed');
273     },
275     /**
276      * Checks if message comes from the loaded authentication extension.
277      * @param {Object} e Payload of the received HTML5 message.
278      * @type {boolean}
279      */
280     isAuthExtMessage_: function(e) {
281       return this.frame_.src &&
282           this.frame_.src.indexOf(e.origin) == 0 &&
283           e.source == this.frame_.contentWindow;
284     },
286     /**
287      * Event handler that is invoked when HTML5 message is received.
288      * @param {object} e Payload of the received HTML5 message.
289      */
290     onMessage_: function(e) {
291       var msg = e.data;
293       // In the inline sign in flow, the embedded gaia webview posts credential
294       // directly to the inline sign in page, because its parent JavaScript
295       // reference points to the top frame of the embedder instead of the sub
296       // frame of the gaia auth extension.
297       if (e.origin == GAIA_ORIGIN && msg.method == 'attemptLogin') {
298         this.email_ = msg.email;
299         this.password_ = msg.password;
300         this.chooseWhatToSync_ = msg.chooseWhatToSync;
301         return;
302       }
304       if (!this.isAuthExtMessage_(e))
305         return;
307       if (msg.method == 'loginUILoaded') {
308         cr.dispatchSimpleEvent(this, 'ready');
309         return;
310       }
312       if (/^complete(Login|Authentication)$|^offlineLogin$/.test(msg.method)) {
313         this.onAuthSuccess_({email: msg.email || this.email_,
314                              password: msg.password || this.password_,
315                              authCode: msg.authCode,
316                              useOffline: msg.method == 'offlineLogin',
317                              chooseWhatToSync: this.chooseWhatToSync_});
318         return;
319       }
321       if (msg.method == 'confirmPassword') {
322         if (this.confirmPasswordCallback_)
323           this.confirmPasswordCallback_();
324         else
325           console.error('GaiaAuthHost: Invalid confirmPasswordCallback_.');
326         return;
327       }
329       if (msg.method == 'noPassword') {
330         if (this.noPasswordCallback_)
331           this.noPasswordCallback_(msg.email);
332         else
333           console.error('GaiaAuthHost: Invalid noPasswordCallback_.');
334         return;
335       }
337       if (msg.method == 'authPageLoaded') {
338         if (this.authPageLoadedCallback_)
339           this.authPageLoadedCallback_(msg.isSAML);
340         return;
341       }
343       if (msg.method == 'reportState') {
344         var newUrl = setQueryParam(location, 'frameUrl', msg.src);
345         if (history.state) {
346           if (history.state.src != msg.src) {
347             history.pushState({src: msg.src}, '', newUrl);
348           }
349         } else {
350           history.replaceState({src: msg.src}, '', newUrl);
351         }
352         return;
353       }
355       if (msg.method == 'switchToFullTab') {
356         chrome.send('switchToFullTab', [msg.url]);
357         return;
358       }
360       console.error('Unknown message method=' + msg.method);
361     },
363     /**
364      * Event handler that is invoked when the history state is changed.
365      * @param {object} e The popstate event being triggered.
366      */
367     onPopState_: function(e) {
368       var state = e.state;
369       if (state) {
370         var msg = {
371           method: 'navigate',
372           src: state.src
373         };
374         this.frame_.contentWindow.postMessage(msg, AUTH_URL_BASE);
375       }
376     }
377   };
379   GaiaAuthHost.SUPPORTED_PARAMS = SUPPORTED_PARAMS;
380   GaiaAuthHost.LOCALIZED_STRING_PARAMS = LOCALIZED_STRING_PARAMS;
381   GaiaAuthHost.AuthMode = AuthMode;
383   return {
384     GaiaAuthHost: GaiaAuthHost
385   };