Roll usrcstplib -> r9048.
[chromium-blink-merge.git] / remoting / webapp / identity.js
blobaae7b396c760cf1ec2901b4f20520f1bf9f2c0f3
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|remoting.OAuth2}
20  */
21 remoting.identity = null;
23 /**
24  * @param {function(function():void):void} consentCallback Callback invoked if
25  *     user consent is required. The callback is passed a continuation function
26  *     which must be called from an interactive event handler (e.g. "click").
27  * @constructor
28  */
29 remoting.Identity = function(consentCallback) {
30   /** @private */
31   this.consentCallback_ = consentCallback;
32   /** @type {?string} @private */
33   this.email_ = null;
34   /** @type {Array.<remoting.Identity.Callbacks>} */
35   this.pendingCallbacks_ = [];
38 /**
39  * Call a function with an access token.
40  *
41  * @param {function(string):void} onOk Function to invoke with access token if
42  *     an access token was successfully retrieved.
43  * @param {function(remoting.Error):void} onError Function to invoke with an
44  *     error code on failure.
45  * @return {void} Nothing.
46  */
47 remoting.Identity.prototype.callWithToken = function(onOk, onError) {
48   this.pendingCallbacks_.push(new remoting.Identity.Callbacks(onOk, onError));
49   if (this.pendingCallbacks_.length == 1) {
50     chrome.identity.getAuthToken(
51         { 'interactive': false },
52         this.onAuthComplete_.bind(this, false));
53   }
56 /**
57  * Remove the cached auth token, if any.
58  *
59  * @param {function():void} onDone Completion callback.
60  * @return {void} Nothing.
61  */
62 remoting.Identity.prototype.removeCachedAuthToken = function(onDone) {
63   /** @param {string} token */
64   var onToken = function(token) {
65     if (token) {
66       chrome.identity.removeCachedAuthToken({ 'token': token }, onDone);
67     } else {
68       onDone();
69     }
70   };
71   chrome.identity.getAuthToken({ 'interactive': false }, onToken);
74 /**
75  * Get the user's email address.
76  *
77  * @param {function(string):void} onOk Callback invoked when the email
78  *     address is available.
79  * @param {function(remoting.Error):void} onError Callback invoked if an
80  *     error occurs.
81  * @return {void} Nothing.
82  */
83 remoting.Identity.prototype.getEmail = function(onOk, onError) {
84   /** @type {remoting.Identity} */
85   var that = this;
86   /** @param {string} email */
87   var onResponse = function(email) {
88     that.email_ = email;
89     onOk(email);
90   };
92   this.callWithToken(
93       remoting.OAuth2Api.getEmail.bind(null, onResponse, onError), onError);
96 /**
97  * Get the user's email address, or null if no successful call to getEmail
98  * has been made.
99  *
100  * @return {?string} The cached email address, if available.
101  */
102 remoting.Identity.prototype.getCachedEmail = function() {
103   return this.email_;
107  * Callback for the getAuthToken API.
109  * @param {boolean} interactive The value of the "interactive" parameter to
110  *     getAuthToken.
111  * @param {?string} token The auth token, or null if the request failed.
112  * @private
113  */
114 remoting.Identity.prototype.onAuthComplete_ = function(interactive, token) {
115   // Pass the token to the callback(s) if it was retrieved successfully.
116   if (token) {
117     while (this.pendingCallbacks_.length > 0) {
118       var callback = /** @type {remoting.Identity.Callbacks} */
119           this.pendingCallbacks_.shift();
120       callback.onOk(token);
121     }
122     return;
123   }
125   // If not, pass an error back to the callback(s) if we've already prompted the
126   // user for permission.
127   if (interactive) {
128     var error_message =
129         chrome.runtime.lastError ? chrome.runtime.lastError.message
130                                  : 'Unknown error.';
131     console.error(error_message);
132     while (this.pendingCallbacks_.length > 0) {
133       var callback = /** @type {remoting.Identity.Callbacks} */
134           this.pendingCallbacks_.shift();
135       callback.onError(remoting.Error.NOT_AUTHENTICATED);
136     }
137     return;
138   }
140   // If there's no token, but we haven't yet prompted for permission, do so
141   // now. The consent callback is responsible for continuing the auth flow.
142   this.consentCallback_(this.onAuthContinue_.bind(this));
146  * Called in response to the user signing in to the web-app.
148  * @private
149  */
150 remoting.Identity.prototype.onAuthContinue_ = function() {
151   chrome.identity.getAuthToken(
152       { 'interactive': true },
153       this.onAuthComplete_.bind(this, true));
157  * Internal representation for pair of callWithToken callbacks.
159  * @param {function(string):void} onOk
160  * @param {function(remoting.Error):void} onError
161  * @constructor
162  * @private
163  */
164 remoting.Identity.Callbacks = function(onOk, onError) {
165   /** @type {function(string):void} */
166   this.onOk = onOk;
167   /** @type {function(remoting.Error):void} */
168   this.onError = onError;
172  * Returns whether the web app has authenticated with the Google services.
174  * @return {boolean}
175  */
176 remoting.Identity.prototype.isAuthenticated = function() {
177   return remoting.identity.email_ != null;