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

Side by Side Diff: remoting/webapp/me2mom/oauth2.js

Issue 9148043: Rename webapp_it2me to remoting_webapp and move it from webapp/me2mom to webapp/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add webapp_it2me back Created 8 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
« no previous file with comments | « remoting/webapp/me2mom/manifest.json ('k') | remoting/webapp/me2mom/oauth2_callback.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 * OAuth2 class that handles retrieval/storage of an OAuth2 token.
8 *
9 * Uses a content script to trampoline the OAuth redirect page back into the
10 * extension context. This works around the lack of native support for
11 * chrome-extensions in OAuth2.
12 */
13
14 'use strict';
15
16 /** @suppress {duplicate} */
17 var remoting = remoting || {};
18
19 /** @type {remoting.OAuth2} */
20 remoting.oauth2 = null;
21
22
23 /** @constructor */
24 remoting.OAuth2 = function() {
25 };
26
27 // Constants representing keys used for storing persistent state.
28 /** @private */
29 remoting.OAuth2.prototype.KEY_REFRESH_TOKEN_ = 'oauth2-refresh-token';
30 /** @private */
31 remoting.OAuth2.prototype.KEY_ACCESS_TOKEN_ = 'oauth2-access-token';
32 /** @private */
33 remoting.OAuth2.prototype.KEY_EMAIL_ = 'remoting-email';
34
35 // Constants for parameters used in retrieving the OAuth2 credentials.
36 /** @private */
37 remoting.OAuth2.prototype.CLIENT_ID_ =
38 '440925447803-2pi3v45bff6tp1rde2f7q6lgbor3o5uj.' +
39 'apps.googleusercontent.com';
40 /** @private */
41 remoting.OAuth2.prototype.CLIENT_SECRET_ = 'W2ieEsG-R1gIA4MMurGrgMc_';
42 /** @private */
43 remoting.OAuth2.prototype.SCOPE_ =
44 'https://www.googleapis.com/auth/chromoting ' +
45 'https://www.googleapis.com/auth/googletalk ' +
46 'https://www.googleapis.com/auth/userinfo#email';
47 /** @private */
48 remoting.OAuth2.prototype.REDIRECT_URI_ =
49 'https://talkgadget.google.com/talkgadget/blank';
50 /** @private */
51 remoting.OAuth2.prototype.OAUTH2_TOKEN_ENDPOINT_ =
52 'https://accounts.google.com/o/oauth2/token';
53
54 /** @return {boolean} True if the app is already authenticated. */
55 remoting.OAuth2.prototype.isAuthenticated = function() {
56 if (this.getRefreshToken()) {
57 return true;
58 }
59 return false;
60 };
61
62 /**
63 * Removes all storage, and effectively unauthenticates the user.
64 *
65 * @return {void} Nothing.
66 */
67 remoting.OAuth2.prototype.clear = function() {
68 window.localStorage.removeItem(this.KEY_REFRESH_TOKEN_);
69 window.localStorage.removeItem(this.KEY_EMAIL_);
70 this.clearAccessToken();
71 };
72
73 /**
74 * @param {string} token The new refresh token.
75 * @return {void} Nothing.
76 */
77 remoting.OAuth2.prototype.setRefreshToken = function(token) {
78 window.localStorage.setItem(this.KEY_REFRESH_TOKEN_, escape(token));
79 this.clearAccessToken();
80 };
81
82 /** @return {?string} The refresh token, if authenticated, or NULL. */
83 remoting.OAuth2.prototype.getRefreshToken = function() {
84 var value = window.localStorage.getItem(this.KEY_REFRESH_TOKEN_);
85 if (typeof value == 'string') {
86 return unescape(value);
87 }
88 return null;
89 };
90
91 /**
92 * @param {string} token The new access token.
93 * @param {number} expiration Expiration time in milliseconds since epoch.
94 * @return {void} Nothing.
95 */
96 remoting.OAuth2.prototype.setAccessToken = function(token, expiration) {
97 var access_token = {'token': token, 'expiration': expiration};
98 window.localStorage.setItem(this.KEY_ACCESS_TOKEN_,
99 JSON.stringify(access_token));
100 };
101
102 /**
103 * Returns the current access token, setting it to a invalid value if none
104 * existed before.
105 *
106 * @private
107 * @return {{token: string, expiration: number}} The current access token, or
108 * an invalid token if not authenticated.
109 */
110 remoting.OAuth2.prototype.getAccessTokenInternal_ = function() {
111 if (!window.localStorage.getItem(this.KEY_ACCESS_TOKEN_)) {
112 // Always be able to return structured data.
113 this.setAccessToken('', 0);
114 }
115 var accessToken = window.localStorage.getItem(this.KEY_ACCESS_TOKEN_);
116 if (typeof accessToken == 'string') {
117 var result = JSON.parse(accessToken);
118 if ('token' in result && 'expiration' in result) {
119 return /** @type {{token: string, expiration: number}} */ result;
120 }
121 }
122 console.log('Invalid access token stored.');
123 return {'token': '', 'expiration': 0};
124 };
125
126 /**
127 * Returns true if the access token is expired, or otherwise invalid.
128 *
129 * Will throw if !isAuthenticated().
130 *
131 * @return {boolean} True if a new access token is needed.
132 */
133 remoting.OAuth2.prototype.needsNewAccessToken = function() {
134 if (!this.isAuthenticated()) {
135 throw 'Not Authenticated.';
136 }
137 var access_token = this.getAccessTokenInternal_();
138 if (!access_token['token']) {
139 return true;
140 }
141 if (Date.now() > access_token['expiration']) {
142 return true;
143 }
144 return false;
145 };
146
147 /**
148 * Returns the current access token.
149 *
150 * Will throw if !isAuthenticated() or needsNewAccessToken().
151 *
152 * @return {string} The access token.
153 */
154 remoting.OAuth2.prototype.getAccessToken = function() {
155 if (this.needsNewAccessToken()) {
156 throw 'Access Token expired.';
157 }
158 return this.getAccessTokenInternal_()['token'];
159 };
160
161 /** @return {void} Nothing. */
162 remoting.OAuth2.prototype.clearAccessToken = function() {
163 window.localStorage.removeItem(this.KEY_ACCESS_TOKEN_);
164 };
165
166 /**
167 * Update state based on token response from the OAuth2 /token endpoint.
168 *
169 * @private
170 * @param {XMLHttpRequest} xhr The XHR object for this request.
171 * @return {void} Nothing.
172 */
173 remoting.OAuth2.prototype.processTokenResponse_ = function(xhr) {
174 if (xhr.status == 200) {
175 var tokens = JSON.parse(xhr.responseText);
176 if ('refresh_token' in tokens) {
177 this.setRefreshToken(tokens['refresh_token']);
178 }
179
180 // Offset by 120 seconds so that we can guarantee that the token
181 // we return will be valid for at least 2 minutes.
182 // If the access token is to be useful, this object must make some
183 // guarantee as to how long the token will be valid for.
184 // The choice of 2 minutes is arbitrary, but that length of time
185 // is part of the contract satisfied by callWithToken().
186 // Offset by a further 30 seconds to account for RTT issues.
187 this.setAccessToken(tokens['access_token'],
188 (tokens['expires_in'] - (120 + 30)) * 1000 + Date.now());
189 } else {
190 console.log('Failed to get tokens. Status: ' + xhr.status +
191 ' response: ' + xhr.responseText);
192 }
193 };
194
195 /**
196 * Asynchronously retrieves a new access token from the server.
197 *
198 * Will throw if !isAuthenticated().
199 *
200 * @param {function(XMLHttpRequest): void} onDone Callback to invoke on
201 * completion.
202 * @return {void} Nothing.
203 */
204 remoting.OAuth2.prototype.refreshAccessToken = function(onDone) {
205 if (!this.isAuthenticated()) {
206 throw 'Not Authenticated.';
207 }
208
209 var parameters = {
210 'client_id': this.CLIENT_ID_,
211 'client_secret': this.CLIENT_SECRET_,
212 'refresh_token': this.getRefreshToken(),
213 'grant_type': 'refresh_token'
214 };
215
216 /** @type {remoting.OAuth2} */
217 var that = this;
218 /** @param {XMLHttpRequest} xhr The XHR reply. */
219 var processTokenResponse = function(xhr) {
220 that.processTokenResponse_(xhr);
221 onDone(xhr);
222 };
223 remoting.xhr.post(this.OAUTH2_TOKEN_ENDPOINT_,
224 processTokenResponse,
225 parameters);
226 };
227
228 /**
229 * Redirect page to get a new OAuth2 Refresh Token.
230 *
231 * @return {void} Nothing.
232 */
233 remoting.OAuth2.prototype.doAuthRedirect = function() {
234 var GET_CODE_URL = 'https://accounts.google.com/o/oauth2/auth?' +
235 remoting.xhr.urlencodeParamHash({
236 'client_id': this.CLIENT_ID_,
237 'redirect_uri': this.REDIRECT_URI_,
238 'scope': this.SCOPE_,
239 'response_type': 'code',
240 'access_type': 'offline',
241 'approval_prompt': 'force'
242 });
243 window.location.replace(GET_CODE_URL);
244 };
245
246 /**
247 * Asynchronously exchanges an authorization code for a refresh token.
248 *
249 * @param {string} code The new refresh token.
250 * @param {function(XMLHttpRequest):void} onDone Callback to invoke on
251 * completion.
252 * @return {void} Nothing.
253 */
254 remoting.OAuth2.prototype.exchangeCodeForToken = function(code, onDone) {
255 var parameters = {
256 'client_id': this.CLIENT_ID_,
257 'client_secret': this.CLIENT_SECRET_,
258 'redirect_uri': this.REDIRECT_URI_,
259 'code': code,
260 'grant_type': 'authorization_code'
261 };
262
263 /** @type {remoting.OAuth2} */
264 var that = this;
265 /** @param {XMLHttpRequest} xhr The XHR reply. */
266 var processTokenResponse = function(xhr) {
267 that.processTokenResponse_(xhr);
268 onDone(xhr);
269 };
270 remoting.xhr.post(this.OAUTH2_TOKEN_ENDPOINT_,
271 processTokenResponse,
272 parameters);
273 };
274
275 /**
276 * Call myfunc with an access token as the only parameter.
277 *
278 * This will refresh the access token if necessary. If the access token
279 * cannot be refreshed, an error is thrown.
280 *
281 * The access token will remain valid for at least 2 minutes.
282 *
283 * @param {function(string):void} myfunc
284 * Function to invoke with access token.
285 * @return {void} Nothing.
286 */
287 remoting.OAuth2.prototype.callWithToken = function(myfunc) {
288 /** @type {remoting.OAuth2} */
289 var that = this;
290 if (remoting.oauth2.needsNewAccessToken()) {
291 remoting.oauth2.refreshAccessToken(function() {
292 if (remoting.oauth2.needsNewAccessToken()) {
293 // If we still need it, we're going to infinite loop.
294 throw 'Unable to get access token.';
295 }
296 myfunc(that.getAccessToken());
297 });
298 return;
299 }
300
301 myfunc(this.getAccessToken());
302 };
303
304 /**
305 * Get the user's email address.
306 *
307 * @param {function(?string):void} setEmail Callback invoked when the email
308 * address is available, or on error.
309 * @return {void} Nothing.
310 */
311 remoting.OAuth2.prototype.getEmail = function(setEmail) {
312 /** @type {remoting.OAuth2} */
313 var that = this;
314 /** @param {XMLHttpRequest} xhr The XHR response. */
315 var onResponse = function(xhr) {
316 that.email = null;
317 if (xhr.status == 200) {
318 // TODO(ajwong): See if we can't find a JSON endpoint.
319 that.email = xhr.responseText.split('&')[0].split('=')[1];
320 }
321 window.localStorage.setItem(that.KEY_EMAIL_, that.email);
322 setEmail(that.email);
323 };
324
325 /** @param {string} token The access token. */
326 var getEmailFromToken = function(token) {
327 var headers = { 'Authorization': 'OAuth ' + token };
328 // TODO(ajwong): Update to new v2 API.
329 remoting.xhr.get('https://www.googleapis.com/userinfo/email',
330 onResponse, '', headers);
331 };
332
333 this.callWithToken(getEmailFromToken);
334 };
335
336 /**
337 * If the user's email address is cached, return it, otherwise return null.
338 *
339 * @return {?string} The email address, if it has been cached by a previous call
340 * to getEmail, otherwise null.
341 */
342 remoting.OAuth2.prototype.getCachedEmail = function() {
343 var value = window.localStorage.getItem(this.KEY_EMAIL_);
344 if (typeof value == 'string') {
345 return value;
346 }
347 return null;
348 };
OLDNEW
« no previous file with comments | « remoting/webapp/me2mom/manifest.json ('k') | remoting/webapp/me2mom/oauth2_callback.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698