• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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} */
13var 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 */
21remoting.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 */
29remoting.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 */
47remoting.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 */
62remoting.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 */
83remoting.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 */
102remoting.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 */
114remoting.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 */
148remoting.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 */
162remoting.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 */
174remoting.Identity.prototype.isAuthenticated = function() {
175  return remoting.identity.email_ != null;
176};
177