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

Side by Side 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, 11 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 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
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));
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
49 if (this.pendingCallbacks_.length == 1) {
50 chrome.experimental.identity.getAuthToken(
51 { 'interactive': false },
52 this.onAuthComplete_.bind(this, false));
53 }
54 };
55
56 /**
57 * Get the user's email address.
58 *
59 * @param {function(string):void} onOk Callback invoked when the email
60 * address is available.
61 * @param {function(remoting.Error):void} onError Callback invoked if an
62 * error occurs.
63 * @return {void} Nothing.
64 */
65 remoting.Identity.prototype.getEmail = function(onOk, onError) {
66 /** @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
67 var that = this;
68 /** @param {XMLHttpRequest} xhr The XHR response. */
69 var onResponse = function(xhr) {
70 var email = null;
71 if (xhr.status == 200) {
72 email = xhr.responseText.split('&')[0].split('=')[1];
73 that.email_ = email;
74 onOk(email);
75 return;
76 }
77 console.error('Unable to get email address:', xhr.status, xhr);
78 if (xhr.status == 401) {
79 onError(remoting.Error.AUTHENTICATION_FAILED);
80 } else {
81 onError(that.interpretUnexpectedXhrStatus_(xhr.status));
82 }
83 };
84
85 /** @param {string} token The access token. */
86 var getEmailFromToken = function(token) {
87 var headers = { 'Authorization': 'OAuth ' + token };
88 // TODO(ajwong): Update to new v2 API.
89 remoting.xhr.get('https://www.googleapis.com/userinfo/email',
90 onResponse, '', headers);
91 };
92
93 this.callWithToken(getEmailFromToken, 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 * Interprets unexpected HTTP response codes to authentication XMLHttpRequests.
108 * The caller should handle the usual expected responses (200, 400) separately.
109 *
110 * @param {number} xhrStatus Status (HTTP response code) of the XMLHttpRequest.
111 * @return {remoting.Error} An error code to be raised.
112 * @private
113 */
114 remoting.Identity.prototype.interpretUnexpectedXhrStatus_ = function(
115 xhrStatus) {
116 // Return AUTHENTICATION_FAILED by default, so that the user can try to
117 // recover from an unexpected failure by signing in again.
118 /** @type {remoting.Error} */
119 var error = remoting.Error.AUTHENTICATION_FAILED;
120 if (xhrStatus == 502 || xhrStatus == 503) {
121 error = remoting.Error.SERVICE_UNAVAILABLE;
122 } else if (xhrStatus == 0) {
123 error = remoting.Error.NETWORK_FAILURE;
124 } else {
125 console.warn('Unexpected authentication response code: ' + xhrStatus);
126 }
127 return error;
128 };
129
130 /**
131 * Callback for the getAuthToken API.
132 *
133 * @param {boolean} interactive The value of the "interactive" parameter to
134 * getAuthToken.
135 * @param {?string} token The auth token, or null if the request failed.
136 * @private
137 */
138 remoting.Identity.prototype.onAuthComplete_ = function(interactive, token) {
139 // Pass the token to the callback(s) if it was retrieved successfully.
140 if (token) {
141 while (this.pendingCallbacks_.length > 0) {
142 var callback = /** @type {remoting.Identity.Callbacks} */
143 this.pendingCallbacks_.shift();
144 callback.onOk(token);
145 }
146 return;
147 }
148
149 // If not, pass an error back to the callback(s) if we've already prompted the
150 // user for permission.
151 // TODO(jamiewalch): Figure out what to do with the error in this case.
152 if (interactive) {
153 console.error(chrome.runtime.lastError);
154 while (this.pendingCallbacks_.length > 0) {
155 var callback = /** @type {remoting.Identity.Callbacks} */
156 this.pendingCallbacks_.shift();
157 callback.onError(remoting.Error.UNEXPECTED);
158 }
159 return;
160 }
161
162 // If there's no token, but we haven't yet prompted for permission, do so
163 // now. The consent callback is responsible for continuing the auth flow.
164 this.consentCallback_(this.onAuthContinue_.bind(this));
165 };
166
167 /**
168 * Called in response to the user signing in to the web-app.
169 *
170 * @private
171 */
172 remoting.Identity.prototype.onAuthContinue_ = function() {
173 chrome.experimental.identity.getAuthToken(
174 { 'interactive': true },
175 this.onAuthComplete_.bind(this, true));
176 };
177
178 /**
179 * Internal representation for pair of callWithToken callbacks.
180 *
181 * @param {function(string):void} onOk
182 * @param {function(remoting.Error):void} onError
183 * @constructor
184 * @private
185 */
186 remoting.Identity.Callbacks = function(onOk, onError) {
187 /** @type {function(string):void} */
188 this.onOk = onOk;
189 /** @type {function(remoting.Error):void} */
190 this.onError = onError;
191 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698