| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @fileoverview |
| * Wrapper class for Chrome's identity API. |
| */ |
| |
| 'use strict'; |
| |
| /** @suppress {duplicate} */ |
| var remoting = remoting || {}; |
| |
| /** |
| * TODO(jamiewalch): Remove remoting.OAuth2 from this type annotation when |
| * the Apps v2 work is complete. |
| * |
| * @type {remoting.Identity|remoting.OAuth2} |
| */ |
| remoting.identity = null; |
| |
| /** |
| * @param {function(function():void):void} consentCallback Callback invoked if |
| * user consent is required. The callback is passed a continuation function |
| * which must be called from an interactive event handler (e.g. "click"). |
| * @constructor |
| */ |
| remoting.Identity = function(consentCallback) { |
| /** @private */ |
| this.consentCallback_ = consentCallback; |
| /** @type {?string} @private */ |
| this.email_ = null; |
| /** @type {Array.<remoting.Identity.Callbacks>} */ |
| this.pendingCallbacks_ = []; |
| }; |
| |
| /** |
| * Call a function with an access token. |
| * |
| * TODO(jamiewalch): Currently, this results in a new GAIA token being minted |
| * each time the function is called. Implement caching functionality unless |
| * getAuthToken starts doing so itself. |
| * |
| * @param {function(string):void} onOk Function to invoke with access token if |
| * an access token was successfully retrieved. |
| * @param {function(remoting.Error):void} onError Function to invoke with an |
| * error code on failure. |
| * @return {void} Nothing. |
| */ |
| remoting.Identity.prototype.callWithToken = function(onOk, onError) { |
| this.pendingCallbacks_.push(new remoting.Identity.Callbacks(onOk, onError)); |
| if (this.pendingCallbacks_.length == 1) { |
| chrome.experimental.identity.getAuthToken( |
| { 'interactive': false }, |
| this.onAuthComplete_.bind(this, false)); |
| } |
| }; |
| |
| /** |
| * Get the user's email address. |
| * |
| * @param {function(string):void} onOk Callback invoked when the email |
| * address is available. |
| * @param {function(remoting.Error):void} onError Callback invoked if an |
| * error occurs. |
| * @return {void} Nothing. |
| */ |
| remoting.Identity.prototype.getEmail = function(onOk, onError) { |
| /** @type {remoting.Identity} */ |
| var that = this; |
| /** @param {string} email */ |
| var onResponse = function(email) { |
| that.email_ = email; |
| onOk(email); |
| }; |
| |
| this.callWithToken( |
| remoting.OAuth2Api.getEmail.bind(null, onResponse, onError), onError); |
| }; |
| |
| /** |
| * Get the user's email address, or null if no successful call to getEmail |
| * has been made. |
| * |
| * @return {?string} The cached email address, if available. |
| */ |
| remoting.Identity.prototype.getCachedEmail = function() { |
| return this.email_; |
| }; |
| |
| /** |
| * Callback for the getAuthToken API. |
| * |
| * @param {boolean} interactive The value of the "interactive" parameter to |
| * getAuthToken. |
| * @param {?string} token The auth token, or null if the request failed. |
| * @private |
| */ |
| remoting.Identity.prototype.onAuthComplete_ = function(interactive, token) { |
| // Pass the token to the callback(s) if it was retrieved successfully. |
| if (token) { |
| while (this.pendingCallbacks_.length > 0) { |
| var callback = /** @type {remoting.Identity.Callbacks} */ |
| this.pendingCallbacks_.shift(); |
| callback.onOk(token); |
| } |
| return; |
| } |
| |
| // If not, pass an error back to the callback(s) if we've already prompted the |
| // user for permission. |
| // TODO(jamiewalch): Figure out what to do with the error in this case. |
| if (interactive) { |
| console.error(chrome.runtime.lastError); |
| while (this.pendingCallbacks_.length > 0) { |
| var callback = /** @type {remoting.Identity.Callbacks} */ |
| this.pendingCallbacks_.shift(); |
| callback.onError(remoting.Error.UNEXPECTED); |
| } |
| return; |
| } |
| |
| // If there's no token, but we haven't yet prompted for permission, do so |
| // now. The consent callback is responsible for continuing the auth flow. |
| this.consentCallback_(this.onAuthContinue_.bind(this)); |
| }; |
| |
| /** |
| * Called in response to the user signing in to the web-app. |
| * |
| * @private |
| */ |
| remoting.Identity.prototype.onAuthContinue_ = function() { |
| chrome.experimental.identity.getAuthToken( |
| { 'interactive': true }, |
| this.onAuthComplete_.bind(this, true)); |
| }; |
| |
| /** |
| * Internal representation for pair of callWithToken callbacks. |
| * |
| * @param {function(string):void} onOk |
| * @param {function(remoting.Error):void} onError |
| * @constructor |
| * @private |
| */ |
| remoting.Identity.Callbacks = function(onOk, onError) { |
| /** @type {function(string):void} */ |
| this.onOk = onOk; |
| /** @type {function(remoting.Error):void} */ |
| this.onError = onError; |
| }; |