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 + ' ; xhr: ', xhr); |
| 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 |