Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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 // TODO(ajwong): This seems like a bad idea. Why are we doing it like this? | |
| 5 var remoting = chrome.extension.getBackgroundPage().remoting; | 6 var remoting = chrome.extension.getBackgroundPage().remoting; |
| 7 | |
| 6 XMPP_LOGIN_NAME = 'xmpp_login'; | 8 XMPP_LOGIN_NAME = 'xmpp_login'; |
| 7 XMPP_TOKEN_NAME = 'xmpp_token'; | 9 XMPP_TOKEN_NAME = 'xmpp_token'; |
| 8 OAUTH2_TOKEN_NAME = 'oauth2_token'; | |
| 9 HOST_PLUGIN_ID = 'host_plugin_id'; | 10 HOST_PLUGIN_ID = 'host_plugin_id'; |
| 10 | 11 |
|
Jamie
2011/05/19 21:50:50
I think this would still be better in a separate f
| |
| 12 // Declare an OAuth2 class to handle retrieval/storage of an OAuth2 token. | |
| 13 // | |
| 14 // Ideally, this should implement the OAuth2 PostMessage flow to avoid needing | |
| 15 // to copy and paste a code, but that does not support extension URL schemes | |
| 16 // quite yet. Instead, we currently use the native app flow with an | |
| 17 // authorization code that the user must cut/paste. | |
| 18 function OAuth2() { | |
| 19 this.OAUTH2_REFRESH_TOKEN_NAME = 'oauth2_refresh_token'; | |
| 20 | |
| 21 this.client_id = encodeURIComponent( | |
| 22 '440925447803-m890isgsr23kdkcu2erd4mirnrjalf98.' + | |
| 23 'apps.googleusercontent.com'); | |
| 24 this.client_secret = encodeURIComponent('TgKrL73H2kJe6Ir0ufp7bf6e'); | |
| 25 this.scope = encodeURIComponent( | |
| 26 'https://www.googleapis.com/auth/chromoting ' + | |
| 27 'https://www.googleapis.com/auth/googletalk'); | |
| 28 this.redirect_uri = encodeURIComponent('urn:ietf:wg:oauth:2.0:oob'); | |
| 29 } | |
| 30 | |
| 31 OAuth2.prototype.isAuthenticated = function() { | |
| 32 if(this.getRefreshToken()) { | |
| 33 return true; | |
| 34 } | |
| 35 return false; | |
| 36 } | |
| 37 | |
| 38 OAuth2.prototype.clear = function() { | |
| 39 remoting.removeItem(this.OAUTH2_REFRESH_TOKEN_NAME); | |
| 40 delete this.access_token; | |
| 41 delete this.access_token_expiration; | |
| 42 } | |
| 43 | |
| 44 OAuth2.prototype.setRefreshToken = function(token) { | |
| 45 remoting.setItem(this.OAUTH2_REFRESH_TOKEN_NAME, token); | |
| 46 } | |
| 47 | |
| 48 OAuth2.prototype.getRefreshToken = function(token) { | |
| 49 return remoting.getItem(this.OAUTH2_REFRESH_TOKEN_NAME); | |
| 50 } | |
| 51 | |
| 52 OAuth2.prototype.setAccessToken = function(token, expiration) { | |
| 53 this.access_token = token; | |
| 54 // Offset by 30 seconds to account for RTT issues. | |
| 55 // TODO(ajwong): See if this is necessary, or of the protocol already | |
| 56 // accounts for RTT. | |
| 57 this.access_token_expiration = expiration - 30000; | |
| 58 } | |
| 59 | |
| 60 OAuth2.prototype.needsNewAccessToken = function() { | |
| 61 if (!this.isAuthenticated()) { | |
| 62 throw "Not Authenticated."; | |
| 63 } | |
| 64 if (!this.access_token) { | |
| 65 return true; | |
| 66 } | |
| 67 if (Date.now() > this.access_token_expiration) { | |
| 68 return true; | |
| 69 } | |
| 70 return false; | |
| 71 } | |
| 72 | |
| 73 OAuth2.prototype.getAccessToken = function() { | |
| 74 if (this.needsNewAccessToken()) { | |
| 75 throw "Access Token expired."; | |
| 76 } | |
| 77 return this.access_token; | |
| 78 } | |
| 79 | |
| 80 OAuth2.prototype.refreshAccessToken = function(on_done) { | |
| 81 if (!this.isAuthenticated()) { | |
| 82 throw "Not Authenticated."; | |
| 83 } | |
| 84 var xhr = new XMLHttpRequest(); | |
| 85 var that = this; | |
| 86 xhr.onreadystatechange = function() { | |
| 87 if (xhr.readyState != 4) { | |
| 88 return; | |
| 89 } | |
| 90 if (xhr.status == 200) { | |
| 91 tokens = JSON.parse(xhr.responseText); | |
| 92 that.setAccessToken(tokens['access_token'], | |
| 93 tokens['expires_in'] * 1000 + Date.now()); | |
| 94 } else { | |
| 95 console.log("Refresh access token failed. Status: " + xhr.status + | |
| 96 " response: " + xhr.responseText); | |
| 97 } | |
| 98 on_done(); | |
| 99 }; | |
| 100 xhr.open('POST', 'https://accounts.google.com/o/oauth2/token', true); | |
| 101 xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); | |
| 102 var post_data = 'client_id=' + this.client_id | |
| 103 + '&client_secret=' + this.client_secret | |
| 104 + '&refresh_token=' + encodeURIComponent(this.getRefreshToken()) | |
| 105 + '&grant_type=refresh_token'; | |
| 106 xhr.send(post_data); | |
| 107 } | |
| 108 | |
| 109 OAuth2.prototype.openOAuth2Window = function() { | |
| 110 var GET_CODE_URL = 'https://accounts.google.com/o/oauth2/auth?' | |
| 111 + 'client_id=' + this.client_id | |
| 112 + '&redirect_uri=' + this.redirect_uri | |
| 113 + '&scope=' + this.scope | |
| 114 + '&response_type=code'; | |
| 115 window.open(GET_CODE_URL); | |
| 116 } | |
| 117 | |
| 118 OAuth2.prototype.exchangeCodeForToken = function(code, on_done) { | |
| 119 var xhr = new XMLHttpRequest(); | |
| 120 var that = this; | |
| 121 xhr.onreadystatechange = function() { | |
| 122 if (xhr.readyState != 4) { | |
| 123 return; | |
| 124 } | |
| 125 if (xhr.status == 200) { | |
| 126 tokens = JSON.parse(xhr.responseText); | |
| 127 that.setRefreshToken(tokens['refresh_token']); | |
| 128 that.setAccessToken(tokens['access_token'], | |
| 129 tokens['expires_in'] + Date.now()); | |
| 130 } else { | |
| 131 console.log("Code exchnage failed. Status: " + xhr.status + | |
| 132 " response: " + xhr.responseText); | |
| 133 } | |
| 134 on_done(); | |
| 135 }; | |
| 136 xhr.open('POST', 'https://accounts.google.com/o/oauth2/token', true); | |
| 137 xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); | |
| 138 var post_data = 'client_id=' + this.client_id | |
| 139 + '&client_secret=' + this.client_secret | |
| 140 + '&redirect_uri=' + this.redirect_uri | |
| 141 + '&code=' + encodeURIComponent(code) | |
| 142 + '&grant_type=authorization_code'; | |
| 143 xhr.send(post_data); | |
| 144 } | |
| 145 | |
| 11 function updateAuthStatus_() { | 146 function updateAuthStatus_() { |
| 12 var oauth1_status = document.getElementById('oauth1_status'); | 147 var oauth2_status = document.getElementById('oauth2_status'); |
| 13 if (remoting.oauth.hasToken()) { | 148 if (remoting.oauth2.isAuthenticated()) { |
| 14 oauth1_status.innerText = 'OK'; | 149 oauth2_status.innerText = 'OK'; |
| 15 oauth1_status.style.color = 'green'; | 150 oauth2_status.style.color = 'green'; |
| 151 document.getElementById('oauth2_code_button').style.display = 'none'; | |
| 152 document.getElementById('oauth2_clear_button').style.display = 'inline'; | |
| 153 document.getElementById('oauth2_form').style.display = 'none'; | |
| 16 } else { | 154 } else { |
| 17 oauth1_status.innerText = 'Unauthorized'; | 155 oauth2_status.innerText = 'Unauthorized'; |
| 18 oauth1_status.style.color = 'red'; | 156 oauth2_status.style.color = 'red'; |
| 157 document.getElementById('oauth2_code_button').style.display = 'inline'; | |
| 158 document.getElementById('oauth2_clear_button').style.display = 'none'; | |
| 159 document.getElementById('oauth2_form').style.display = 'inline'; | |
| 19 } | 160 } |
| 20 var xmpp_status = document.getElementById('xmpp_status'); | 161 var xmpp_status = document.getElementById('xmpp_status'); |
| 21 if (remoting.getItem(XMPP_TOKEN_NAME) && remoting.getItem(XMPP_LOGIN_NAME)) { | 162 if (remoting.getItem(XMPP_TOKEN_NAME) && remoting.getItem(XMPP_LOGIN_NAME)) { |
| 22 document.getElementById('xmpp_clear').style.display = 'inline'; | 163 document.getElementById('xmpp_clear').style.display = 'inline'; |
| 23 document.getElementById('xmpp_form').style.display = 'none'; | 164 document.getElementById('xmpp_form').style.display = 'none'; |
| 24 xmpp_status.innerText = 'OK'; | 165 xmpp_status.innerText = 'OK'; |
| 25 xmpp_status.style.color = 'green'; | 166 xmpp_status.style.color = 'green'; |
| 26 remoting.xmppAuthToken = remoting.getItem(XMPP_TOKEN_NAME); | 167 remoting.xmppAuthToken = remoting.getItem(XMPP_TOKEN_NAME); |
| 27 } else { | 168 } else { |
| 28 document.getElementById('xmpp_clear').style.display = 'none'; | 169 document.getElementById('xmpp_clear').style.display = 'none'; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 71 | 212 |
| 72 function initAuthPanel_() { | 213 function initAuthPanel_() { |
| 73 updateAuthStatus_(); | 214 updateAuthStatus_(); |
| 74 resetXmppErrors_(); | 215 resetXmppErrors_(); |
| 75 } | 216 } |
| 76 | 217 |
| 77 function initBackgroundFuncs_() { | 218 function initBackgroundFuncs_() { |
| 78 remoting.getItem = chrome.extension.getBackgroundPage().getItem; | 219 remoting.getItem = chrome.extension.getBackgroundPage().getItem; |
| 79 remoting.setItem = chrome.extension.getBackgroundPage().setItem; | 220 remoting.setItem = chrome.extension.getBackgroundPage().setItem; |
| 80 remoting.removeItem = chrome.extension.getBackgroundPage().removeItem; | 221 remoting.removeItem = chrome.extension.getBackgroundPage().removeItem; |
| 81 remoting.oauth = chrome.extension.getBackgroundPage().oauth; | 222 remoting.oauth2 = new OAuth2(); |
| 82 } | 223 } |
| 83 | 224 |
| 84 function authorizeXmpp(form) { | 225 function authorizeXmpp(form) { |
| 85 var xhr = new XMLHttpRequest(); | 226 var xhr = new XMLHttpRequest(); |
| 86 var captcha_result = readAndClearCaptcha_(form); | 227 var captcha_result = readAndClearCaptcha_(form); |
| 87 | 228 |
| 88 xhr.onreadystatechange = function() { | 229 xhr.onreadystatechange = function() { |
| 89 if (xhr.readyState != 4) { | 230 if (xhr.readyState != 4) { |
| 90 return; | 231 return; |
| 91 } | 232 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 '&Email=' + encodeURIComponent(form['xmpp_username'].value) + | 265 '&Email=' + encodeURIComponent(form['xmpp_username'].value) + |
| 125 '&Passwd=' + encodeURIComponent(form['xmpp_password'].value); | 266 '&Passwd=' + encodeURIComponent(form['xmpp_password'].value); |
| 126 | 267 |
| 127 if (captcha_result[0]) { | 268 if (captcha_result[0]) { |
| 128 post_data += '&logintoken=' + encodeURIComponent(captcha_result[0]) + | 269 post_data += '&logintoken=' + encodeURIComponent(captcha_result[0]) + |
| 129 '&logincaptcha=' + encodeURIComponent(captcha_result[1]); | 270 '&logincaptcha=' + encodeURIComponent(captcha_result[1]); |
| 130 } | 271 } |
| 131 xhr.send(post_data); | 272 xhr.send(post_data); |
| 132 } | 273 } |
| 133 | 274 |
| 134 function authorizeOAuth1() { | 275 function authorizeOAuth2(code) { |
| 135 remoting.oauth.authorize(updateAuthStatus_); | 276 remoting.oauth2.exchangeCodeForToken(code, updateAuthStatus_); |
| 136 } | 277 } |
| 137 | 278 |
| 138 function clearOAuth1() { | 279 function clearOAuth2() { |
| 139 remoting.oauth.clearTokens(); | 280 remoting.oauth2.clear(); |
| 140 updateAuthStatus_(); | 281 updateAuthStatus_(); |
| 141 } | 282 } |
| 142 | 283 |
| 143 function clearXmpp() { | 284 function clearXmpp() { |
| 144 remoting.removeItem(XMPP_TOKEN_NAME); | 285 remoting.removeItem(XMPP_TOKEN_NAME); |
| 145 updateAuthStatus_(); | 286 updateAuthStatus_(); |
| 146 } | 287 } |
| 147 | 288 |
| 148 // Show the div with id |mode| and hide those with other ids in |modes|. | 289 // Show the div with id |mode| and hide those with other ids in |modes|. |
| 149 function setMode_(mode, modes) { | 290 function setMode_(mode, modes) { |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 236 } else { | 377 } else { |
| 237 invalid.style.display = 'none'; | 378 invalid.style.display = 'none'; |
| 238 other.style.display = 'block'; | 379 other.style.display = 'block'; |
| 239 var responseNode = document.getElementById('server_response'); | 380 var responseNode = document.getElementById('server_response'); |
| 240 responseNode.innerText = responseString + ' (' + responseCode + ')'; | 381 responseNode.innerText = responseString + ' (' + responseCode + ')'; |
| 241 } | 382 } |
| 242 remoting.accessCode = ''; | 383 remoting.accessCode = ''; |
| 243 setClientMode('connect_failed'); | 384 setClientMode('connect_failed'); |
| 244 } | 385 } |
| 245 | 386 |
| 246 function parseServerResponse_(reply, xhr) { | 387 function parseServerResponse_(xhr) { |
| 247 if (xhr.status == 200) { | 388 if (xhr.status == 200) { |
| 248 var host = JSON.parse(xhr.responseText); | 389 var host = JSON.parse(xhr.responseText); |
| 249 if (host.data && host.data.jabberId) { | 390 if (host.data && host.data.jabberId) { |
| 250 remoting.hostjid = host.data.jabberId; | 391 remoting.hostjid = host.data.jabberId; |
| 251 startSession_(); | 392 startSession_(); |
| 252 return; | 393 return; |
| 253 } | 394 } |
| 254 } | 395 } |
| 255 showConnectError_(xhr.status, xhr.responseText); | 396 showConnectError_(xhr.status, xhr.responseText); |
| 256 } | 397 } |
| 257 | 398 |
| 399 function resolveSupportId(support_id) { | |
| 400 var xhr = new XMLHttpRequest(); | |
| 401 xhr.onreadystatechange = function() { | |
| 402 if (xhr.readyState != 4) { | |
| 403 return; | |
| 404 } | |
| 405 parseServerResponse_(xhr); | |
| 406 }; | |
| 407 | |
| 408 xhr.open('GET', | |
| 409 'https://www.googleapis.com/chromoting/v1/support-hosts/' + | |
| 410 encodeURIComponent(support_id), | |
| 411 true); | |
| 412 xhr.setRequestHeader('Authorization', | |
| 413 'OAuth ' + remoting.oauth2.getAccessToken()); | |
| 414 xhr.send(null); | |
| 415 } | |
| 416 | |
| 258 function tryConnect(form) { | 417 function tryConnect(form) { |
| 259 remoting.accessCode = form['access_code_entry'].value; | 418 remoting.accessCode = form['access_code_entry'].value; |
| 260 // TODO(jamiewalch): Since the mapping from (SupportId, HostSecret) to | 419 // TODO(jamiewalch): Since the mapping from (SupportId, HostSecret) to |
| 261 // AccessCode is not yet defined, assume it's hyphen-separated for now. | 420 // AccessCode is not yet defined, assume it's hyphen-separated for now. |
| 262 var parts = remoting.accessCode.split('-'); | 421 var parts = remoting.accessCode.split('-'); |
| 263 if (parts.length != 2) { | 422 if (parts.length != 2) { |
| 264 showConnectError_(404); | 423 showConnectError_(404); |
| 265 } else { | 424 } else { |
| 266 setClientMode('connecting'); | 425 setClientMode('connecting'); |
| 267 var urlBase = 'https://www.googleapis.com/chromoting/v1/support-hosts/'; | 426 if (remoting.oauth2.needsNewAccessToken()) { |
| 268 remoting.oauth.sendSignedRequest( | 427 remoting.oauth2.refreshAccessToken(function() { |
| 269 urlBase + '' + encodeURIComponent(parts[0]) + '', | 428 resolveSupportId(parts[0]); |
| 270 parseServerResponse_); | 429 }); |
| 430 return; | |
| 431 } else { | |
| 432 resolveSupportId(parts[0]); | |
| 433 } | |
| 271 } | 434 } |
| 272 } | 435 } |
| 273 | 436 |
| 274 function cancelConnect() { | 437 function cancelConnect() { |
| 275 remoting.accessCode = ''; | 438 remoting.accessCode = ''; |
| 276 setClientMode('unconnected'); | 439 setClientMode('unconnected'); |
| 277 } | 440 } |
| OLD | NEW |