OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview | 6 * @fileoverview |
7 * OAuth2 class that handles retrieval/storage of an OAuth2 token. | 7 * OAuth2 class that handles retrieval/storage of an OAuth2 token. |
8 * | 8 * |
9 * Uses a content script to trampoline the OAuth redirect page back into the | 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 | 10 * extension context. This works around the lack of native support for |
(...skipping 10 matching lines...) Expand all Loading... | |
21 | 21 |
22 | 22 |
23 /** @constructor */ | 23 /** @constructor */ |
24 remoting.OAuth2 = function() { | 24 remoting.OAuth2 = function() { |
25 }; | 25 }; |
26 | 26 |
27 // Constants representing keys used for storing persistent state. | 27 // Constants representing keys used for storing persistent state. |
28 /** @private */ | 28 /** @private */ |
29 remoting.OAuth2.prototype.KEY_REFRESH_TOKEN_ = 'oauth2-refresh-token'; | 29 remoting.OAuth2.prototype.KEY_REFRESH_TOKEN_ = 'oauth2-refresh-token'; |
30 /** @private */ | 30 /** @private */ |
31 remoting.OAuth2.prototype.KEY_REFRESH_TOKEN_REVOKABLE_ = | |
32 'oauth2-refresh-token-revokable'; | |
33 /** @private */ | |
31 remoting.OAuth2.prototype.KEY_ACCESS_TOKEN_ = 'oauth2-access-token'; | 34 remoting.OAuth2.prototype.KEY_ACCESS_TOKEN_ = 'oauth2-access-token'; |
32 /** @private */ | 35 /** @private */ |
33 remoting.OAuth2.prototype.KEY_EMAIL_ = 'remoting-email'; | 36 remoting.OAuth2.prototype.KEY_EMAIL_ = 'remoting-email'; |
34 | 37 |
35 // Constants for parameters used in retrieving the OAuth2 credentials. | 38 // Constants for parameters used in retrieving the OAuth2 credentials. |
36 /** @private */ | 39 /** @private */ |
37 remoting.OAuth2.prototype.SCOPE_ = | 40 remoting.OAuth2.prototype.SCOPE_ = |
38 'https://www.googleapis.com/auth/chromoting ' + | 41 'https://www.googleapis.com/auth/chromoting ' + |
39 'https://www.googleapis.com/auth/googletalk ' + | 42 'https://www.googleapis.com/auth/googletalk ' + |
40 'https://www.googleapis.com/auth/userinfo#email'; | 43 'https://www.googleapis.com/auth/userinfo#email'; |
41 /** @private */ | 44 /** @private */ |
42 remoting.OAuth2.prototype.OAUTH2_TOKEN_ENDPOINT_ = | 45 remoting.OAuth2.prototype.OAUTH2_TOKEN_ENDPOINT_ = |
43 'https://accounts.google.com/o/oauth2/token'; | 46 'https://accounts.google.com/o/oauth2/token'; |
44 | 47 /** @private */ |
48 remoting.OAuth2.prototype.OAUTH2_REVOKE_TOKEN_ENDPOINT_ = | |
49 'https://accounts.google.com/o/oauth2/revoke'; | |
45 /** @return {boolean} True if the app is already authenticated. */ | 50 /** @return {boolean} True if the app is already authenticated. */ |
46 remoting.OAuth2.prototype.isAuthenticated = function() { | 51 remoting.OAuth2.prototype.isAuthenticated = function() { |
47 if (this.getRefreshToken()) { | 52 if (this.getRefreshToken_()) { |
48 return true; | 53 return true; |
49 } | 54 } |
50 return false; | 55 return false; |
51 }; | 56 }; |
52 | 57 |
53 /** | 58 /** |
54 * Removes all storage, and effectively unauthenticates the user. | 59 * Removes all storage, and effectively unauthenticates the user. |
55 * | 60 * |
56 * @return {void} Nothing. | 61 * @return {void} Nothing. |
57 */ | 62 */ |
58 remoting.OAuth2.prototype.clear = function() { | 63 remoting.OAuth2.prototype.clear = function() { |
59 window.localStorage.removeItem(this.KEY_REFRESH_TOKEN_); | |
60 window.localStorage.removeItem(this.KEY_EMAIL_); | 64 window.localStorage.removeItem(this.KEY_EMAIL_); |
61 this.clearAccessToken(); | 65 this.clearAccessToken(); |
66 this.clearRefreshToken_(); | |
62 }; | 67 }; |
63 | 68 |
64 /** | 69 /** |
70 * Sets the refresh token. | |
71 * | |
72 * This method also marks the token as revokable, so that this object will | |
73 * revoke the token when it no longer needs it. | |
74 * | |
65 * @param {string} token The new refresh token. | 75 * @param {string} token The new refresh token. |
66 * @return {void} Nothing. | 76 * @return {void} Nothing. |
67 */ | 77 */ |
68 remoting.OAuth2.prototype.setRefreshToken = function(token) { | 78 remoting.OAuth2.prototype.setRefreshToken = function(token) { |
69 window.localStorage.setItem(this.KEY_REFRESH_TOKEN_, escape(token)); | 79 window.localStorage.setItem(this.KEY_REFRESH_TOKEN_, escape(token)); |
80 window.localStorage.setItem(this.KEY_REFRESH_TOKEN_REVOKABLE_, true); | |
70 this.clearAccessToken(); | 81 this.clearAccessToken(); |
71 }; | 82 }; |
72 | 83 |
73 /** @return {?string} The refresh token, if authenticated, or NULL. */ | 84 /** |
74 remoting.OAuth2.prototype.getRefreshToken = function() { | 85 * Gets the refresh token. |
86 * | |
87 * This method also marks the refresh token as not revokable, so that this | |
88 * object will not revoke the token when it no longer needs it. After this | |
89 * object has exported the token, it cannot know whether it is still in use | |
90 * when this object no longer needs it. | |
91 * | |
92 * @return {?string} The refresh token, if authenticated, or NULL. | |
93 */ | |
94 remoting.OAuth2.prototype.exportRefreshToken = function() { | |
95 window.localStorage.removeItem(this.KEY_REFRESH_TOKEN_REVOKABLE_); | |
96 return this.getRefreshToken_(); | |
97 }; | |
98 | |
99 /** | |
100 * @return {?string} The refresh token, if authenticated, or NULL. | |
101 * @private | |
102 */ | |
103 remoting.OAuth2.prototype.getRefreshToken_ = function() { | |
75 var value = window.localStorage.getItem(this.KEY_REFRESH_TOKEN_); | 104 var value = window.localStorage.getItem(this.KEY_REFRESH_TOKEN_); |
76 if (typeof value == 'string') { | 105 if (typeof value == 'string') { |
77 return unescape(value); | 106 return unescape(value); |
78 } | 107 } |
79 return null; | 108 return null; |
80 }; | 109 }; |
81 | 110 |
82 /** | 111 /** |
112 * Clears the refresh token. | |
113 * | |
114 * @return {void} Nothing. | |
115 * @private | |
116 */ | |
117 remoting.OAuth2.prototype.clearRefreshToken_ = function() { | |
118 if (window.localStorage.getItem(this.KEY_REFRESH_TOKEN_REVOKABLE_)) { | |
119 this.revokeToken_(this.getRefreshToken_()); | |
120 } | |
121 window.localStorage.removeItem(this.KEY_REFRESH_TOKEN_); | |
122 window.localStorage.removeItem(this.KEY_REFRESH_TOKEN_REVOKABLE_); | |
123 }; | |
124 | |
125 /** | |
83 * @param {string} token The new access token. | 126 * @param {string} token The new access token. |
84 * @param {number} expiration Expiration time in milliseconds since epoch. | 127 * @param {number} expiration Expiration time in milliseconds since epoch. |
85 * @return {void} Nothing. | 128 * @return {void} Nothing. |
86 */ | 129 */ |
87 remoting.OAuth2.prototype.setAccessToken = function(token, expiration) { | 130 remoting.OAuth2.prototype.setAccessToken = function(token, expiration) { |
88 var access_token = {'token': token, 'expiration': expiration}; | 131 var access_token = {'token': token, 'expiration': expiration}; |
89 window.localStorage.setItem(this.KEY_ACCESS_TOKEN_, | 132 window.localStorage.setItem(this.KEY_ACCESS_TOKEN_, |
90 JSON.stringify(access_token)); | 133 JSON.stringify(access_token)); |
91 }; | 134 }; |
92 | 135 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
193 * @return {void} Nothing. | 236 * @return {void} Nothing. |
194 */ | 237 */ |
195 remoting.OAuth2.prototype.refreshAccessToken = function(onDone) { | 238 remoting.OAuth2.prototype.refreshAccessToken = function(onDone) { |
196 if (!this.isAuthenticated()) { | 239 if (!this.isAuthenticated()) { |
197 throw 'Not Authenticated.'; | 240 throw 'Not Authenticated.'; |
198 } | 241 } |
199 | 242 |
200 var parameters = { | 243 var parameters = { |
201 'client_id': this.CLIENT_ID_, | 244 'client_id': this.CLIENT_ID_, |
202 'client_secret': this.CLIENT_SECRET_, | 245 'client_secret': this.CLIENT_SECRET_, |
203 'refresh_token': this.getRefreshToken(), | 246 'refresh_token': this.getRefreshToken_(), |
204 'grant_type': 'refresh_token' | 247 'grant_type': 'refresh_token' |
205 }; | 248 }; |
206 | 249 |
207 /** @type {remoting.OAuth2} */ | 250 /** @type {remoting.OAuth2} */ |
208 var that = this; | 251 var that = this; |
209 /** @param {XMLHttpRequest} xhr The XHR reply. */ | 252 /** @param {XMLHttpRequest} xhr The XHR reply. */ |
210 var processTokenResponse = function(xhr) { | 253 var processTokenResponse = function(xhr) { |
211 that.processTokenResponse_(xhr); | 254 that.processTokenResponse_(xhr); |
212 onDone(xhr); | 255 onDone(xhr); |
213 }; | 256 }; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
257 var processTokenResponse = function(xhr) { | 300 var processTokenResponse = function(xhr) { |
258 that.processTokenResponse_(xhr); | 301 that.processTokenResponse_(xhr); |
259 onDone(xhr); | 302 onDone(xhr); |
260 }; | 303 }; |
261 remoting.xhr.post(this.OAUTH2_TOKEN_ENDPOINT_, | 304 remoting.xhr.post(this.OAUTH2_TOKEN_ENDPOINT_, |
262 processTokenResponse, | 305 processTokenResponse, |
263 parameters); | 306 parameters); |
264 }; | 307 }; |
265 | 308 |
266 /** | 309 /** |
310 * Revokes a refresh or an access token. | |
311 * | |
312 * @param {string?} token An access or refresh token. | |
313 * @return {void} Nothing. | |
314 * @private | |
315 */ | |
316 remoting.OAuth2.prototype.revokeToken_ = function(token) { | |
317 if (!token || (token.length == 0)) { | |
318 return; | |
319 } | |
320 var parameters = { 'token': token }; | |
321 | |
322 /** @param {XMLHttpRequest} xhr The XHR reply. */ | |
323 var processResponse = function(xhr) { | |
324 if (xhr.status != 200) { | |
325 console.log('Failed to revoke token. Status: ' + xhr.status + | |
326 ' response: ' + xhr.responseText); | |
Jamie
2012/04/16 17:04:45
I think I'd probably log the xhr object itself her
simonmorris
2012/04/16 22:37:51
Done.
| |
327 } | |
328 }; | |
329 remoting.xhr.post(this.OAUTH2_REVOKE_TOKEN_ENDPOINT_, | |
330 processResponse, | |
331 parameters); | |
332 }; | |
333 | |
334 /** | |
267 * Call myfunc with an access token as the only parameter. | 335 * Call myfunc with an access token as the only parameter. |
268 * | 336 * |
269 * This will refresh the access token if necessary. If the access token | 337 * This will refresh the access token if necessary. If the access token |
270 * cannot be refreshed, an error is thrown. | 338 * cannot be refreshed, an error is thrown. |
271 * | 339 * |
272 * The access token will remain valid for at least 2 minutes. | 340 * The access token will remain valid for at least 2 minutes. |
273 * | 341 * |
274 * @param {function(string):void} myfunc | 342 * @param {function(string):void} myfunc |
275 * Function to invoke with access token. | 343 * Function to invoke with access token. |
276 * @return {void} Nothing. | 344 * @return {void} Nothing. |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
330 * @return {?string} The email address, if it has been cached by a previous call | 398 * @return {?string} The email address, if it has been cached by a previous call |
331 * to getEmail, otherwise null. | 399 * to getEmail, otherwise null. |
332 */ | 400 */ |
333 remoting.OAuth2.prototype.getCachedEmail = function() { | 401 remoting.OAuth2.prototype.getCachedEmail = function() { |
334 var value = window.localStorage.getItem(this.KEY_EMAIL_); | 402 var value = window.localStorage.getItem(this.KEY_EMAIL_); |
335 if (typeof value == 'string') { | 403 if (typeof value == 'string') { |
336 return value; | 404 return value; |
337 } | 405 } |
338 return null; | 406 return null; |
339 }; | 407 }; |
OLD | NEW |