Rewrite AndroidSyncSettings to be significantly simpler.
[chromium-blink-merge.git] / remoting / webapp / crd / js / identity.js
blob28258168aad4a887bdd3ed35123cd7d54c4ac2b2
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.
5 /**
6  * @fileoverview
7  * Wrapper class for Chrome's identity API.
8  */
10 'use strict';
12 /** @suppress {duplicate} */
13 var remoting = remoting || {};
15 /**
16  * TODO(jamiewalch): Remove remoting.OAuth2 from this type annotation when
17  * the Apps v2 work is complete.
18  *
19  * @type {remoting.Identity}
20  */
21 remoting.identity = null;
23 /**
24  * @param {remoting.Identity.ConsentDialog=} opt_consentDialog
25  * @constructor
26  */
27 remoting.Identity = function(opt_consentDialog) {
28   /** @private */
29   this.consentDialog_ = opt_consentDialog;
30   /** @type {string} @private */
31   this.email_ = '';
32   /** @type {string} @private */
33   this.fullName_ = '';
34   /** @type {base.Deferred<string>} */
35   this.authTokenDeferred_ = null;
38 /**
39  * chrome.identity.getAuthToken should be initiated from user interactions if
40  * called with interactive equals true.  This interface prompts a dialog for
41  * the user's consent.
42  *
43  * @interface
44  */
45 remoting.Identity.ConsentDialog = function() {};
47 /**
48  * @return {Promise} A Promise that resolves when permission to start an
49  *   interactive flow is granted.
50  */
51 remoting.Identity.ConsentDialog.prototype.show = function() {};
53 /**
54  * Gets an access token.
55  *
56  * @return {!Promise<string>} A promise resolved with an access token
57  *     or rejected with a remoting.Error.
58  */
59 remoting.Identity.prototype.getToken = function() {
60   /** @const */
61   var that = this;
63   if (this.authTokenDeferred_ == null) {
64     this.authTokenDeferred_ = new base.Deferred();
65     chrome.identity.getAuthToken(
66         { 'interactive': false },
67         that.onAuthComplete_.bind(that, false));
68   }
69   return this.authTokenDeferred_.promise();
72 /**
73  * Gets a fresh access token.
74  *
75  * @return {!Promise<string>} A promise resolved with an access token
76  *     or rejected with a remoting.Error.
77  */
78 remoting.Identity.prototype.getNewToken = function() {
79   /** @type {remoting.Identity} */
80   var that = this;
82   return this.getToken().then(function(/** string */ token) {
83     return new Promise(function(resolve, reject) {
84       chrome.identity.removeCachedAuthToken({'token': token }, function() {
85         resolve(that.getToken());
86       });
87     });
88   });
91 /**
92  * Removes the cached auth token, if any.
93  *
94  * @return {!Promise<null>} A promise resolved with the operation completes.
95  */
96 remoting.Identity.prototype.removeCachedAuthToken = function() {
97   return new Promise(function(resolve, reject) {
98     /** @param {string} token */
99     var onToken = function(token) {
100       if (token) {
101         chrome.identity.removeCachedAuthToken(
102             {'token': token}, resolve.bind(null, null));
103       } else {
104         resolve(null);
105       }
106     };
107     chrome.identity.getAuthToken({'interactive': false}, onToken);
108   });
112  * Gets the user's email address and full name.  The full name will be
113  * null unless the webapp has requested and been granted the
114  * userinfo.profile permission.
116  * TODO(jrw): Type declarations say the name can't be null.  Are the
117  * types wrong, or is the documentation wrong?
119  * @return {!Promise<{email:string, name:string}>} Promise
120  *     resolved with the user's email address and full name, or rejected
121  *     with a remoting.Error.
122  */
123 remoting.Identity.prototype.getUserInfo = function() {
124   if (this.isAuthenticated()) {
125     /**
126      * The temp variable is needed to work around a compiler bug.
127      * @type {{email: string, name: string}}
128      */
129     var result = {email: this.email_, name: this.fullName_};
130     return Promise.resolve(result);
131   }
133   /** @type {remoting.Identity} */
134   var that = this;
136   return this.getToken().then(function(token) {
137     return new Promise(function(resolve, reject) {
138       /**
139        * @param {string} email
140        * @param {string} name
141        */
142       var onResponse = function(email, name) {
143         that.email_ = email;
144         that.fullName_ = name;
145         resolve({email: email, name: name});
146       };
148       remoting.oauth2Api.getUserInfo(onResponse, reject, token);
149     });
150   });
154  * Gets the user's email address.
156  * @return {!Promise<string>} Promise resolved with the user's email
157  *     address or rejected with a remoting.Error.
158  */
159 remoting.Identity.prototype.getEmail = function() {
160   this.getUserInfo().then(function(userInfo) {
161     return userInfo.email;
162   });
166  * Gets the user's email address, or null if no successful call to
167  * getUserInfo has been made.
169  * @return {?string} The cached email address, if available.
170  */
171 remoting.Identity.prototype.getCachedEmail = function() {
172   return this.email_;
176  * Gets the user's full name.
178  * This will return null if either:
179  *   No successful call to getUserInfo has been made, or
180  *   The webapp doesn't have permission to access this value.
182  * @return {?string} The cached user's full name, if available.
183  */
184 remoting.Identity.prototype.getCachedUserFullName = function() {
185   return this.fullName_;
189  * Callback for the getAuthToken API.
191  * @param {boolean} interactive The value of the "interactive" parameter to
192  *     getAuthToken.
193  * @param {?string} token The auth token, or null if the request failed.
194  * @private
195  */
196 remoting.Identity.prototype.onAuthComplete_ = function(interactive, token) {
197   var authTokenDeferred = this.authTokenDeferred_;
198   if (authTokenDeferred == null) {
199     return;
200   }
201   this.authTokenDeferred_ = null;
203   // Pass the token to the callback(s) if it was retrieved successfully.
204   if (token) {
205     authTokenDeferred.resolve(token);
206     return;
207   }
209   // If not, pass an error back to the callback(s) if we've already prompted the
210   // user for permission.
211   if (interactive) {
212     var error_message =
213         chrome.runtime.lastError ? chrome.runtime.lastError.message
214                                  : 'Unknown error.';
215     console.error(error_message);
216     authTokenDeferred.reject(remoting.Error.NOT_AUTHENTICATED);
217     return;
218   }
220   // If there's no token, but we haven't yet prompted for permission, do so
221   // now.
222   var that = this;
223   var showConsentDialog =
224       (this.consentDialog_) ? this.consentDialog_.show() : Promise.resolve();
225   showConsentDialog.then(function() {
226     chrome.identity.getAuthToken(
227         {'interactive': true}, that.onAuthComplete_.bind(that, true));
228   });
232  * Returns whether the web app has authenticated with the Google services.
234  * @return {boolean}
235  */
236 remoting.Identity.prototype.isAuthenticated = function() {
237   return remoting.identity.email_ !== '';