• 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  if (interactive) {
128    console.error(chrome.runtime.lastError);
129    while (this.pendingCallbacks_.length > 0) {
130      var callback = /** @type {remoting.Identity.Callbacks} */
131          this.pendingCallbacks_.shift();
132      callback.onError(remoting.Error.NOT_AUTHENTICATED);
133    }
134    return;
135  }
136
137  // If there's no token, but we haven't yet prompted for permission, do so
138  // now. The consent callback is responsible for continuing the auth flow.
139  this.consentCallback_(this.onAuthContinue_.bind(this));
140};
141
142/**
143 * Called in response to the user signing in to the web-app.
144 *
145 * @private
146 */
147remoting.Identity.prototype.onAuthContinue_ = function() {
148  chrome.identity.getAuthToken(
149      { 'interactive': true },
150      this.onAuthComplete_.bind(this, true));
151};
152
153/**
154 * Internal representation for pair of callWithToken callbacks.
155 *
156 * @param {function(string):void} onOk
157 * @param {function(remoting.Error):void} onError
158 * @constructor
159 * @private
160 */
161remoting.Identity.Callbacks = function(onOk, onError) {
162  /** @type {function(string):void} */
163  this.onOk = onOk;
164  /** @type {function(remoting.Error):void} */
165  this.onError = onError;
166};
167
168/**
169 * Returns whether the web app has authenticated with the Google services.
170 *
171 * @return {boolean}
172 */
173remoting.Identity.prototype.isAuthenticated = function() {
174  return remoting.identity.email_ != null;
175};
176