Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2193)

Unified Diff: remoting/webapp/identity.js

Issue 11769002: Apps v2 identity integration. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reviewer comments. Created 7 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: remoting/webapp/identity.js
diff --git a/remoting/webapp/identity.js b/remoting/webapp/identity.js
new file mode 100644
index 0000000000000000000000000000000000000000..ef559e62f35f00f3072a1e058ecf47b3cfb48ccf
--- /dev/null
+++ b/remoting/webapp/identity.js
@@ -0,0 +1,191 @@
+// 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 a "click" event handler.
Wez 2013/01/07 23:52:44 nit: Is that the actual requirement, or will any s
Jamie 2013/01/08 22:09:10 I've only tested it with a "click" handler, but I
+ * @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.
+ *
+ * @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));
rmsousa 2013/01/05 05:21:11 Do we really want to do mint a new token on every
Jamie 2013/01/07 23:52:20 Thanks for checking on this. I think you're right
Wez 2013/01/07 23:52:44 Access tokens last about an hour, I think, so duri
Jamie 2013/01/08 22:09:10 I don't think it does--that's what Renato's commen
+ 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} */
rmsousa 2013/01/05 05:21:11 Did you mean to remove these from oauth2.js? (If y
Jamie 2013/01/07 23:52:20 oauth2.js will be deleted completely once we're a
Wez 2013/01/07 23:54:20 How will we fetch a refresh token to configure the
+ var that = this;
+ /** @param {XMLHttpRequest} xhr The XHR response. */
+ var onResponse = function(xhr) {
+ var email = null;
+ if (xhr.status == 200) {
+ email = xhr.responseText.split('&')[0].split('=')[1];
+ that.email_ = email;
+ onOk(email);
+ return;
+ }
+ console.error('Unable to get email address:', xhr.status, xhr);
+ if (xhr.status == 401) {
+ onError(remoting.Error.AUTHENTICATION_FAILED);
+ } else {
+ onError(that.interpretUnexpectedXhrStatus_(xhr.status));
+ }
+ };
+
+ /** @param {string} token The access token. */
+ var getEmailFromToken = function(token) {
+ var headers = { 'Authorization': 'OAuth ' + token };
+ // TODO(ajwong): Update to new v2 API.
+ remoting.xhr.get('https://www.googleapis.com/userinfo/email',
+ onResponse, '', headers);
+ };
+
+ this.callWithToken(getEmailFromToken, 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_;
+};
+
+/**
+ * Interprets unexpected HTTP response codes to authentication XMLHttpRequests.
+ * The caller should handle the usual expected responses (200, 400) separately.
+ *
+ * @param {number} xhrStatus Status (HTTP response code) of the XMLHttpRequest.
+ * @return {remoting.Error} An error code to be raised.
+ * @private
+ */
+remoting.Identity.prototype.interpretUnexpectedXhrStatus_ = function(
+ xhrStatus) {
+ // Return AUTHENTICATION_FAILED by default, so that the user can try to
+ // recover from an unexpected failure by signing in again.
+ /** @type {remoting.Error} */
+ var error = remoting.Error.AUTHENTICATION_FAILED;
+ if (xhrStatus == 502 || xhrStatus == 503) {
+ error = remoting.Error.SERVICE_UNAVAILABLE;
+ } else if (xhrStatus == 0) {
+ error = remoting.Error.NETWORK_FAILURE;
+ } else {
+ console.warn('Unexpected authentication response code: ' + xhrStatus);
+ }
+ return error;
+};
+
+/**
+ * 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;
+};

Powered by Google App Engine
This is Rietveld 408576698