Home | History | Annotate | Download | only in webapp
      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  * @fileoverview
      7  * Wrapper class for Chrome's identity API.
      8  */
      9 
     10 'use strict';
     11 
     12 /** @suppress {duplicate} */
     13 var remoting = remoting || {};
     14 
     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;
     22 
     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_ = [];
     36 };
     37 
     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   }
     54 };
     55 
     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);
     72 };
     73 
     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   };
     91 
     92   this.callWithToken(
     93       remoting.OAuth2Api.getEmail.bind(null, onResponse, onError), onError);
     94 };
     95 
     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_;
    104 };
    105 
    106 /**
    107  * Callback for the getAuthToken API.
    108  *
    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   }
    124 
    125   // If not, pass an error back to the callback(s) if we've already prompted the
    126   // user for permission.
    127   // TODO(jamiewalch): Figure out what to do with the error in this case.
    128   if (interactive) {
    129     console.error(chrome.runtime.lastError);
    130     while (this.pendingCallbacks_.length > 0) {
    131       var callback = /** @type {remoting.Identity.Callbacks} */
    132           this.pendingCallbacks_.shift();
    133       callback.onError(remoting.Error.UNEXPECTED);
    134     }
    135     return;
    136   }
    137 
    138   // If there's no token, but we haven't yet prompted for permission, do so
    139   // now. The consent callback is responsible for continuing the auth flow.
    140   this.consentCallback_(this.onAuthContinue_.bind(this));
    141 };
    142 
    143 /**
    144  * Called in response to the user signing in to the web-app.
    145  *
    146  * @private
    147  */
    148 remoting.Identity.prototype.onAuthContinue_ = function() {
    149   chrome.identity.getAuthToken(
    150       { 'interactive': true },
    151       this.onAuthComplete_.bind(this, true));
    152 };
    153 
    154 /**
    155  * Internal representation for pair of callWithToken callbacks.
    156  *
    157  * @param {function(string):void} onOk
    158  * @param {function(remoting.Error):void} onError
    159  * @constructor
    160  * @private
    161  */
    162 remoting.Identity.Callbacks = function(onOk, onError) {
    163   /** @type {function(string):void} */
    164   this.onOk = onOk;
    165   /** @type {function(remoting.Error):void} */
    166   this.onError = onError;
    167 };
    168 
    169 /**
    170  * Returns whether the web app has authenticated with the Google services.
    171  *
    172  * @return {boolean}
    173  */
    174 remoting.Identity.prototype.isAuthenticated = function() {
    175   return remoting.identity.email_ != null;
    176 };
    177