Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / gaia_auth / main.js
1 // Copyright (c) 2012 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  * Authenticator class wraps the communications between Gaia and its host.
7  */
8 function Authenticator() {
9 }
10
11 /**
12  * Gaia auth extension url origin.
13  * @type {string}
14  */
15 Authenticator.THIS_EXTENSION_ORIGIN =
16     'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik';
17
18 /**
19  * Singleton getter of Authenticator.
20  * @return {Object} The singleton instance of Authenticator.
21  */
22 Authenticator.getInstance = function() {
23   if (!Authenticator.instance_) {
24     Authenticator.instance_ = new Authenticator();
25   }
26   return Authenticator.instance_;
27 };
28
29 Authenticator.prototype = {
30   email_: null,
31   password_: null,
32   attemptToken_: null,
33
34   // Input params from extension initialization URL.
35   inputLang_: undefined,
36   intputEmail_: undefined,
37
38   isSAMLFlow_: false,
39   isSAMLEnabled_: false,
40   supportChannel_: null,
41
42   GAIA_URL: 'https://accounts.google.com/',
43   GAIA_PAGE_PATH: 'ServiceLogin?skipvpage=true&sarp=1&rm=hide',
44   PARENT_PAGE: 'chrome://oobe/',
45   SERVICE_ID: 'chromeoslogin',
46   CONTINUE_URL: Authenticator.THIS_EXTENSION_ORIGIN + '/success.html',
47   CONSTRAINED_FLOW_SOURCE: 'chrome',
48
49   initialize: function() {
50     var params = getUrlSearchParams(location.search);
51     this.parentPage_ = params.parentPage || this.PARENT_PAGE;
52     this.gaiaUrl_ = params.gaiaUrl || this.GAIA_URL;
53     this.gaiaPath_ = params.gaiaPath || this.GAIA_PAGE_PATH;
54     this.inputLang_ = params.hl;
55     this.inputEmail_ = params.email;
56     this.service_ = params.service || this.SERVICE_ID;
57     this.continueUrl_ = params.continueUrl || this.CONTINUE_URL;
58     this.desktopMode_ = params.desktopMode == '1';
59     this.isConstrainedWindow_ = params.constrained == '1';
60     this.initialFrameUrl_ = params.frameUrl || this.constructInitialFrameUrl_();
61     this.initialFrameUrlWithoutParams_ = stripParams(this.initialFrameUrl_);
62
63     document.addEventListener('DOMContentLoaded', this.onPageLoad_.bind(this));
64     document.addEventListener('enableSAML', this.onEnableSAML_.bind(this));
65   },
66
67   isGaiaMessage_: function(msg) {
68     // Not quite right, but good enough.
69     return this.gaiaUrl_.indexOf(msg.origin) == 0 ||
70            this.GAIA_URL.indexOf(msg.origin) == 0;
71   },
72
73   isInternalMessage_: function(msg) {
74     return msg.origin == Authenticator.THIS_EXTENSION_ORIGIN;
75   },
76
77   isParentMessage_: function(msg) {
78     return msg.origin == this.parentPage_;
79   },
80
81   constructInitialFrameUrl_: function() {
82     var url = this.gaiaUrl_ + this.gaiaPath_;
83
84     url = appendParam(url, 'service', this.service_);
85     url = appendParam(url, 'continue', this.continueUrl_);
86     if (this.inputLang_)
87       url = appendParam(url, 'hl', this.inputLang_);
88     if (this.inputEmail_)
89       url = appendParam(url, 'Email', this.inputEmail_);
90     if (this.isConstrainedWindow_)
91       url = appendParam(url, 'source', this.CONSTRAINED_FLOW_SOURCE);
92     return url;
93   },
94
95   onPageLoad_: function() {
96     window.addEventListener('message', this.onMessage.bind(this), false);
97
98     var gaiaFrame = $('gaia-frame');
99     gaiaFrame.src = this.initialFrameUrl_;
100
101     if (this.desktopMode_) {
102       var handler = function() {
103         this.onLoginUILoaded_();
104         gaiaFrame.removeEventListener('load', handler);
105
106         this.initDesktopChannel_();
107       }.bind(this);
108       gaiaFrame.addEventListener('load', handler);
109     }
110   },
111
112   initDesktopChannel_: function() {
113     this.supportChannel_ = new Channel();
114     this.supportChannel_.connect('authMain');
115
116     var channelConnected = false;
117     this.supportChannel_.registerMessage('channelConnected', function() {
118       channelConnected = true;
119
120       this.supportChannel_.send({
121         name: 'initDesktopFlow',
122         gaiaUrl: this.gaiaUrl_,
123         continueUrl: stripParams(this.continueUrl_),
124         isConstrainedWindow: this.isConstrainedWindow_
125       });
126       this.supportChannel_.registerMessage(
127         'switchToFullTab', this.switchToFullTab_.bind(this));
128       this.supportChannel_.registerMessage(
129         'completeLogin', this.completeLogin_.bind(this));
130     }.bind(this));
131
132     window.setTimeout(function() {
133       if (!channelConnected) {
134         // Re-initialize the channel if it is not connected properly, e.g.
135         // connect may be called before background script started running.
136         this.initDesktopChannel_();
137       }
138     }.bind(this), 200);
139   },
140
141   /**
142    * Invoked when the login UI is initialized or reset.
143    */
144   onLoginUILoaded_: function() {
145     var msg = {
146       'method': 'loginUILoaded'
147     };
148     window.parent.postMessage(msg, this.parentPage_);
149   },
150
151   /**
152    * Invoked when the background script sends a message to indicate that the
153    * current content does not fit in a constrained window.
154    * @param {Object=} opt_extraMsg Optional extra info to send.
155    */
156   switchToFullTab_: function(msg) {
157     var parentMsg = {
158       'method': 'switchToFullTab',
159       'url': msg.url
160     };
161     window.parent.postMessage(parentMsg, this.parentPage_);
162   },
163
164   /**
165    * Invoked when the signin flow is complete.
166    * @param {Object=} opt_extraMsg Optional extra info to send.
167    */
168   completeLogin_: function(opt_extraMsg) {
169     var msg = {
170       'method': 'completeLogin',
171       'email': (opt_extraMsg && opt_extraMsg.email) || this.email_,
172       'password': this.password_,
173       'usingSAML': this.isSAMLFlow_,
174       'chooseWhatToSync': this.chooseWhatToSync_ || false,
175       'skipForNow': opt_extraMsg && opt_extraMsg.skipForNow,
176       'sessionIndex': opt_extraMsg && opt_extraMsg.sessionIndex
177     };
178     window.parent.postMessage(msg, this.parentPage_);
179     if (this.isSAMLEnabled_)
180       this.supportChannel_.send({name: 'resetAuth'});
181   },
182
183   /**
184    * Invoked when 'enableSAML' event is received to initialize SAML support.
185    */
186   onEnableSAML_: function() {
187     this.isSAMLEnabled_ = true;
188     this.isSAMLFlow_ = false;
189
190     if (!this.supportChannel_) {
191       this.supportChannel_ = new Channel();
192       this.supportChannel_.connect('authMain');
193     }
194
195     this.supportChannel_.registerMessage(
196         'onAuthPageLoaded', this.onAuthPageLoaded_.bind(this));
197     this.supportChannel_.registerMessage(
198         'apiCall', this.onAPICall_.bind(this));
199     this.supportChannel_.send({
200       name: 'setGaiaUrl',
201       gaiaUrl: this.gaiaUrl_
202     });
203   },
204
205   /**
206    * Invoked when the background page sends 'onHostedPageLoaded' message.
207    * @param {!Object} msg Details sent with the message.
208    */
209   onAuthPageLoaded_: function(msg) {
210     var isSAMLPage = msg.url.indexOf(this.gaiaUrl_) != 0;
211
212     if (isSAMLPage && !this.isSAMLFlow_) {
213       // GAIA redirected to a SAML login page. The credentials provided to this
214       // page will determine what user gets logged in. The credentials obtained
215       // from the GAIA login from are no longer relevant and can be discarded.
216       this.isSAMLFlow_ = true;
217       this.email_ = null;
218       this.password_ = null;
219     }
220
221     window.parent.postMessage({
222       'method': 'authPageLoaded',
223       'isSAML': this.isSAMLFlow_,
224       'domain': extractDomain(msg.url)
225     }, this.parentPage_);
226   },
227
228   /**
229    * Invoked when one of the credential passing API methods is called by a SAML
230    * provider.
231    * @param {!Object} msg Details of the API call.
232    */
233   onAPICall_: function(msg) {
234     var call = msg.call;
235     if (call.method == 'add') {
236       this.apiToken_ = call.token;
237       this.email_ = call.user;
238       this.password_ = call.password;
239     } else if (call.method == 'confirm') {
240       if (call.token != this.apiToken_)
241         console.error('Authenticator.onAPICall_: token mismatch');
242     } else {
243       console.error('Authenticator.onAPICall_: unknown message');
244     }
245   },
246
247   onConfirmLogin_: function() {
248     if (!this.isSAMLFlow_) {
249       this.completeLogin_();
250       return;
251     }
252
253     var apiUsed = !!this.password_;
254
255     // Retrieve the e-mail address of the user who just authenticated from GAIA.
256     window.parent.postMessage({method: 'retrieveAuthenticatedUserEmail',
257                                attemptToken: this.attemptToken_,
258                                apiUsed: apiUsed},
259                               this.parentPage_);
260
261     if (!apiUsed) {
262       this.supportChannel_.sendWithCallback(
263           {name: 'getScrapedPasswords'},
264           function(passwords) {
265             if (passwords.length == 0) {
266               window.parent.postMessage(
267                   {method: 'noPassword', email: this.email_},
268                   this.parentPage_);
269             } else {
270               window.parent.postMessage({method: 'confirmPassword',
271                                          email: this.email_,
272                                          passwordCount: passwords.length},
273                                         this.parentPage_);
274             }
275           }.bind(this));
276     }
277   },
278
279   maybeCompleteSAMLLogin_: function() {
280     // SAML login is complete when the user's e-mail address has been retrieved
281     // from GAIA and the user has successfully confirmed the password.
282     if (this.email_ !== null && this.password_ !== null)
283       this.completeLogin_();
284   },
285
286   onVerifyConfirmedPassword_: function(password) {
287     this.supportChannel_.sendWithCallback(
288         {name: 'getScrapedPasswords'},
289         function(passwords) {
290           for (var i = 0; i < passwords.length; ++i) {
291             if (passwords[i] == password) {
292               this.password_ = passwords[i];
293               this.maybeCompleteSAMLLogin_();
294               return;
295             }
296           }
297           window.parent.postMessage(
298               {method: 'confirmPassword', email: this.email_},
299               this.parentPage_);
300         }.bind(this));
301   },
302
303   onMessage: function(e) {
304     var msg = e.data;
305     if (msg.method == 'attemptLogin' && this.isGaiaMessage_(e)) {
306       this.email_ = msg.email;
307       this.password_ = msg.password;
308       this.attemptToken_ = msg.attemptToken;
309       this.chooseWhatToSync_ = msg.chooseWhatToSync;
310       this.isSAMLFlow_ = false;
311       if (this.isSAMLEnabled_)
312         this.supportChannel_.send({name: 'startAuth'});
313     } else if (msg.method == 'clearOldAttempts' && this.isGaiaMessage_(e)) {
314       this.email_ = null;
315       this.password_ = null;
316       this.attemptToken_ = null;
317       this.isSAMLFlow_ = false;
318       this.onLoginUILoaded_();
319       if (this.isSAMLEnabled_)
320         this.supportChannel_.send({name: 'resetAuth'});
321     } else if (msg.method == 'setAuthenticatedUserEmail' &&
322                this.isParentMessage_(e)) {
323       if (this.attemptToken_ == msg.attemptToken) {
324         this.email_ = msg.email;
325         this.maybeCompleteSAMLLogin_();
326       }
327     } else if (msg.method == 'confirmLogin' && this.isInternalMessage_(e)) {
328       if (this.attemptToken_ == msg.attemptToken)
329         this.onConfirmLogin_();
330       else
331         console.error('Authenticator.onMessage: unexpected attemptToken!?');
332     } else if (msg.method == 'verifyConfirmedPassword' &&
333                this.isParentMessage_(e)) {
334       this.onVerifyConfirmedPassword_(msg.password);
335     } else if (msg.method == 'navigate' &&
336                this.isParentMessage_(e)) {
337       $('gaia-frame').src = msg.src;
338     } else if (msg.method == 'redirectToSignin' &&
339                this.isParentMessage_(e)) {
340       $('gaia-frame').src = this.constructInitialFrameUrl_();
341     } else {
342        console.error('Authenticator.onMessage: unknown message + origin!?');
343     }
344   }
345 };
346
347 Authenticator.getInstance().initialize();