summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-20 01:38:42 +0000
committerajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-20 01:38:42 +0000
commit0a0d6cc68ed093cd31e1ebd4fedb453f7b6df127 (patch)
tree5ea5090db4bcf7dfc242ab66c1770954ac005433 /remoting
parent15a245c06daf2b80475d3121e3be3e261b582486 (diff)
downloadchromium_src-0a0d6cc68ed093cd31e1ebd4fedb453f7b6df127.zip
chromium_src-0a0d6cc68ed093cd31e1ebd4fedb453f7b6df127.tar.gz
chromium_src-0a0d6cc68ed093cd31e1ebd4fedb453f7b6df127.tar.bz2
Basic OAuth2 support using the native app flow.
Currently, OAuth2 does not support redirection to chrome-extension URLs. So, temporarily, we will use the native app flow. In the future, this should be replaced with the "PostMessage" flow. BUG=none TEST=Invoke javascript functions by hand and see that we get an access token. Review URL: http://codereview.chromium.org/7046012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86034 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/tools/keygen.py22
-rw-r--r--remoting/webapp/me2mom/background.html2
-rw-r--r--remoting/webapp/me2mom/background.js10
-rw-r--r--remoting/webapp/me2mom/choice.html57
-rw-r--r--remoting/webapp/me2mom/chrome_ex_oauth.html27
-rw-r--r--remoting/webapp/me2mom/chrome_ex_oauth.js593
-rw-r--r--remoting/webapp/me2mom/chrome_ex_oauthsimple.js458
-rw-r--r--remoting/webapp/me2mom/manifest.json1
-rw-r--r--remoting/webapp/me2mom/oauth2.js138
-rw-r--r--remoting/webapp/me2mom/remoting.js64
10 files changed, 233 insertions, 1139 deletions
diff --git a/remoting/tools/keygen.py b/remoting/tools/keygen.py
index bdc488c..deaeae1 100644
--- a/remoting/tools/keygen.py
+++ b/remoting/tools/keygen.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -10,14 +10,14 @@ if SCRIPT_PATH == "":
SCRIPT_PATH = os.getcwd()
PATHS_TO_TRY = [
- '\\..\\..\\build\\Debug\\chromoting_host_keygen.exe',
- '\\..\\..\\build\\Release\\chromoting_host_keygen.exe',
- '\\..\\Debug\\chromoting_host_keygen.exe',
- '\\..\\Release\\chromoting_host_keygen.exe',
- '/../../xcodebuild/Debug/chromoting_host_keygen',
- '/../../xcodebuild/Release/chromoting_host_keygen',
- '/../../out/Debug/chromoting_host_keygen',
- '/../../out/Release/chromoting_host_keygen']
+ '\\..\\..\\build\\Debug\\remoting_host_keygen.exe',
+ '\\..\\..\\build\\Release\\remoting_host_keygen.exe',
+ '\\..\\Debug\\remoting_host_keygen.exe',
+ '\\..\\Release\\remoting_host_keygen.exe',
+ '/../../xcodebuild/Debug/remoting_host_keygen',
+ '/../../xcodebuild/Release/remoting_host_keygen',
+ '/../../out/Debug/remoting_host_keygen',
+ '/../../out/Release/remoting_host_keygen']
KEYGEN_PATH = None
for path in PATHS_TO_TRY:
@@ -26,7 +26,7 @@ for path in PATHS_TO_TRY:
break
if not KEYGEN_PATH:
- raise Exception("Unable to find chromoting_host_keygen. Please build it " +
+ raise Exception("Unable to find remoting_host_keygen. Please build it " +
"and try again")
def generateRSAKeyPair():
@@ -35,5 +35,5 @@ def generateRSAKeyPair():
pipe = os.popen(KEYGEN_PATH)
out = pipe.readlines()
if len(out) != 2:
- raise Exception("chromoting_host_keygen failed.")
+ raise Exception("remoting_host_keygen failed.")
return (out[0].strip(), out[1].strip())
diff --git a/remoting/webapp/me2mom/background.html b/remoting/webapp/me2mom/background.html
index 3cadf1e..3859031 100644
--- a/remoting/webapp/me2mom/background.html
+++ b/remoting/webapp/me2mom/background.html
@@ -5,7 +5,5 @@ Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<html>
- <script type="text/javascript" src="chrome_ex_oauthsimple.js"></script>
- <script type="text/javascript" src="chrome_ex_oauth.js"></script>
<script type="text/javascript" src="background.js"></script>
</html>
diff --git a/remoting/webapp/me2mom/background.js b/remoting/webapp/me2mom/background.js
index 8a59d19..0da714b 100644
--- a/remoting/webapp/me2mom/background.js
+++ b/remoting/webapp/me2mom/background.js
@@ -20,13 +20,3 @@ function removeItem(key) {
function clearAll() {
window.localStorage.clear();
}
-
-var oauth = ChromeExOAuth.initBackgroundPage({
- 'request_url': 'https://www.google.com/accounts/OAuthGetRequestToken',
- 'authorize_url': 'https://www.google.com/accounts/OAuthAuthorizeToken',
- 'access_url': 'https://www.google.com/accounts/OAuthGetAccessToken',
- 'consumer_key': 'anonymous',
- 'consumer_secret': 'anonymous',
- 'scope': 'https://www.googleapis.com/auth/chromoting',
- 'app_name': 'Remoting WebApp'
-});
diff --git a/remoting/webapp/me2mom/choice.html b/remoting/webapp/me2mom/choice.html
index 9011065..3c44172 100644
--- a/remoting/webapp/me2mom/choice.html
+++ b/remoting/webapp/me2mom/choice.html
@@ -10,6 +10,7 @@ found in the LICENSE file.
<head>
<link rel="stylesheet" type="text/css" href="main.css" />
<script type="text/javascript" src="remoting.js"></script>
+ <script type="text/javascript" src="oauth2.js"></script>
<title>Select Role</title>
</head>
@@ -17,31 +18,45 @@ found in the LICENSE file.
<!-- Auth panel -->
<div id="auth_panel">
- Chromoting OAuth1 Token: <span id="oauth1_status"></span>
- <button onclick="authorizeOAuth1();">Authorize</button>
- <button onclick="clearOAuth1();">Clear</button >
- <br />
- XMPP Token: <span id="xmpp_status"></span>
- <button onclick="clearXmpp();" id="xmpp_clear" style="display:none;">
+ OAuth2 Token: <span id="oauth2_status"></span>
+ <button onclick="remoting.oauth2.openOAuth2Window();"
+ id="oauth2_code_button">
+ Open OAuth2 Window
+ </button>
+ <button onclick="clearOAuth2();" id="oauth2_clear_button">
Clear
</button>
- <form id='xmpp_form' action=""
- onsubmit="authorizeXmpp(this); return false;">
- <label for="xmpp_username">Email:</label>
- <input type="text" name="xmpp_username" id="xmpp_username" />
- <label for="xmpp_password">App-specific Password:</label>
- <input type="password" name="xmpp_password" id="xmpp_password" />
- <div id="xmpp_captcha" style="display:none;">
- <img style="display:block;" id="xmpp_captcha_img" />
- <input type="hidden" name="xmpp_captcha_token" />
- <input type="text" name="xmpp_captcha_result" />
- </div>
+ <form id='oauth2_form' action=""
+ onsubmit="authorizeOAuth2(this['oauth2_code'].value); return false;"
+ style="display:none">
+ <label for="auth2_code">OAuth2 Code (from window):</label>
+ <input type="text" name="oauth2_code" id="oauth2_code" />
<input type="submit"/>
</form>
- <span id="xmpp_last_error" style="display:none"></span>
- <iframe id="xmpp_error" style="display:none">
- <p> No iframe support
- </iframe>
+ <div id="xmpp_div">
+ XMPP Token: <span id="xmpp_status"></span>
+ <button onclick="clearXmpp();" id="xmpp_clear" style="display:none;">
+ Clear
+ </button>
+
+ <form id='xmpp_form' action=""
+ onsubmit="authorizeXmpp(this); return false;">
+ <label for="xmpp_username">Email:</label>
+ <input type="text" name="xmpp_username" id="xmpp_username" />
+ <label for="xmpp_password">App-specific Password:</label>
+ <input type="password" name="xmpp_password" id="xmpp_password" />
+ <div id="xmpp_captcha" style="display:none;">
+ <img style="display:block;" id="xmpp_captcha_img" />
+ <input type="hidden" name="xmpp_captcha_token" />
+ <input type="text" name="xmpp_captcha_result" />
+ </div>
+ <input type="submit"/>
+ </form>
+ <span id="xmpp_last_error" style="display:none"></span>
+ <iframe id="xmpp_error" style="display:none">
+ <p> No iframe support
+ </iframe>
+ </div>
</div>
<!-- Host UI -->
diff --git a/remoting/webapp/me2mom/chrome_ex_oauth.html b/remoting/webapp/me2mom/chrome_ex_oauth.html
deleted file mode 100644
index 912f891..0000000
--- a/remoting/webapp/me2mom/chrome_ex_oauth.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!DOCTYPE html>
-<!--
- * Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
- * source code is governed by a BSD-style license that can be found in the
- * LICENSE file.
--->
-<html>
- <head>
- <title>OAuth Redirect Page</title>
- <style type="text/css">
- body {
- font: 16px Arial;
- color: #333;
- }
- </style>
- <script type="text/javascript" src="chrome_ex_oauthsimple.js"></script>
- <script type="text/javascript" src="chrome_ex_oauth.js"></script>
- <script type="text/javascript">
- function onLoad() {
- ChromeExOAuth.initCallbackPage();
- };
- </script>
- </head>
- <body onload="onLoad();">
- Redirecting...
- </body>
-</html>
diff --git a/remoting/webapp/me2mom/chrome_ex_oauth.js b/remoting/webapp/me2mom/chrome_ex_oauth.js
deleted file mode 100644
index 7e13710..0000000
--- a/remoting/webapp/me2mom/chrome_ex_oauth.js
+++ /dev/null
@@ -1,593 +0,0 @@
-/**
- * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this
- * source code is governed by a BSD-style license that can be found in the
- * LICENSE file.
- */
-
-/**
- * Constructor - no need to invoke directly, call initBackgroundPage instead.
- * @constructor
- * @param {String} url_request_token The OAuth request token URL.
- * @param {String} url_auth_token The OAuth authorize token URL.
- * @param {String} url_access_token The OAuth access token URL.
- * @param {String} consumer_key The OAuth consumer key.
- * @param {String} consumer_secret The OAuth consumer secret.
- * @param {String} oauth_scope The OAuth scope parameter.
- * @param {Object} opt_args Optional arguments. Recognized parameters:
- * "app_name" {String} Name of the current application
- * "callback_page" {String} If you renamed chrome_ex_oauth.html, the name
- * this file was renamed to.
- */
-function ChromeExOAuth(url_request_token, url_auth_token, url_access_token,
- consumer_key, consumer_secret, oauth_scope, opt_args) {
- this.url_request_token = url_request_token;
- this.url_auth_token = url_auth_token;
- this.url_access_token = url_access_token;
- this.consumer_key = consumer_key;
- this.consumer_secret = consumer_secret;
- this.oauth_scope = oauth_scope;
- this.app_name = opt_args && opt_args['app_name'] ||
- "ChromeExOAuth Library";
- this.key_token = "oauth_token";
- this.key_token_secret = "oauth_token_secret";
- this.callback_page = opt_args && opt_args['callback_page'] ||
- "chrome_ex_oauth.html";
- this.auth_params = {};
- if (opt_args && opt_args['auth_params']) {
- for (key in opt_args['auth_params']) {
- if (opt_args['auth_params'].hasOwnProperty(key)) {
- this.auth_params[key] = opt_args['auth_params'][key];
- }
- }
- }
-};
-
-/*******************************************************************************
- * PUBLIC API METHODS
- * Call these from your background page.
- ******************************************************************************/
-
-/**
- * Initializes the OAuth helper from the background page. You must call this
- * before attempting to make any OAuth calls.
- * @param {Object} oauth_config Configuration parameters in a JavaScript object.
- * The following parameters are recognized:
- * "request_url" {String} OAuth request token URL.
- * "authorize_url" {String} OAuth authorize token URL.
- * "access_url" {String} OAuth access token URL.
- * "consumer_key" {String} OAuth consumer key.
- * "consumer_secret" {String} OAuth consumer secret.
- * "scope" {String} OAuth access scope.
- * "app_name" {String} Application name.
- * "auth_params" {Object} Additional parameters to pass to the
- * Authorization token URL. For an example, 'hd', 'hl', 'btmpl':
- * http://code.google.com/apis/accounts/docs/OAuth_ref.html#GetAuth
- * @return {ChromeExOAuth} An initialized ChromeExOAuth object.
- */
-ChromeExOAuth.initBackgroundPage = function(oauth_config) {
- window.chromeExOAuthConfig = oauth_config;
- window.chromeExOAuth = ChromeExOAuth.fromConfig(oauth_config);
- window.chromeExOAuthRedirectStarted = false;
- window.chromeExOAuthRequestingAccess = false;
-
- var url_match = chrome.extension.getURL(window.chromeExOAuth.callback_page);
- var tabs = {};
- chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
- if (changeInfo.url &&
- changeInfo.url.substr(0, url_match.length) === url_match &&
- changeInfo.url != tabs[tabId] &&
- window.chromeExOAuthRequestingAccess == false) {
- chrome.tabs.create({ 'url' : changeInfo.url }, function(tab) {
- tabs[tab.id] = tab.url;
- chrome.tabs.remove(tabId);
- });
- }
- });
-
- return window.chromeExOAuth;
-};
-
-/**
- * Authorizes the current user with the configued API. You must call this
- * before calling sendSignedRequest.
- * @param {Function} callback A function to call once an access token has
- * been obtained. This callback will be passed the following arguments:
- * token {String} The OAuth access token.
- * secret {String} The OAuth access token secret.
- */
-ChromeExOAuth.prototype.authorize = function(callback) {
- if (this.hasToken()) {
- callback(this.getToken(), this.getTokenSecret());
- } else {
- window.chromeExOAuthOnAuthorize = function(token, secret) {
- callback(token, secret);
- };
- chrome.tabs.create({ 'url' :chrome.extension.getURL(this.callback_page) });
- }
-};
-
-/**
- * Clears any OAuth tokens stored for this configuration. Effectively a
- * "logout" of the configured OAuth API.
- */
-ChromeExOAuth.prototype.clearTokens = function() {
- delete localStorage[this.key_token + encodeURI(this.oauth_scope)];
- delete localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
-};
-
-/**
- * Returns whether a token is currently stored for this configuration.
- * Effectively a check to see whether the current user is "logged in" to
- * the configured OAuth API.
- * @return {Boolean} True if an access token exists.
- */
-ChromeExOAuth.prototype.hasToken = function() {
- return !!this.getToken();
-};
-
-/**
- * Makes an OAuth-signed HTTP request with the currently authorized tokens.
- * @param {String} url The URL to send the request to. Querystring parameters
- * should be omitted.
- * @param {Function} callback A function to be called once the request is
- * completed. This callback will be passed the following arguments:
- * responseText {String} The text response.
- * xhr {XMLHttpRequest} The XMLHttpRequest object which was used to
- * send the request. Useful if you need to check response status
- * code, etc.
- * @param {Object} opt_params Additional parameters to configure the request.
- * The following parameters are accepted:
- * "method" {String} The HTTP method to use. Defaults to "GET".
- * "body" {String} A request body to send. Defaults to null.
- * "parameters" {Object} Query parameters to include in the request.
- * "headers" {Object} Additional headers to include in the request.
- */
-ChromeExOAuth.prototype.sendSignedRequest = function(url, callback,
- opt_params) {
- var method = opt_params && opt_params['method'] || 'GET';
- var body = opt_params && opt_params['body'] || null;
- var params = opt_params && opt_params['parameters'] || {};
- var headers = opt_params && opt_params['headers'] || {};
-
- var signedUrl = this.signURL(url, method, params);
-
- ChromeExOAuth.sendRequest(method, signedUrl, headers, body, function (xhr) {
- if (xhr.readyState == 4) {
- callback(xhr.responseText, xhr);
- }
- });
-};
-
-/**
- * Adds the required OAuth parameters to the given url and returns the
- * result. Useful if you need a signed url but don't want to make an XHR
- * request.
- * @param {String} method The http method to use.
- * @param {String} url The base url of the resource you are querying.
- * @param {Object} opt_params Query parameters to include in the request.
- * @return {String} The base url plus any query params plus any OAuth params.
- */
-ChromeExOAuth.prototype.signURL = function(url, method, opt_params) {
- var token = this.getToken();
- var secret = this.getTokenSecret();
- if (!token || !secret) {
- throw new Error("No oauth token or token secret");
- }
-
- var params = opt_params || {};
-
- var result = OAuthSimple().sign({
- action : method,
- path : url,
- parameters : params,
- signatures: {
- consumer_key : this.consumer_key,
- shared_secret : this.consumer_secret,
- oauth_secret : secret,
- oauth_token: token
- }
- });
-
- return result.signed_url;
-};
-
-/**
- * Generates the Authorization header based on the oauth parameters.
- * @param {String} url The base url of the resource you are querying.
- * @param {Object} opt_params Query parameters to include in the request.
- * @return {String} An Authorization header containing the oauth_* params.
- */
-ChromeExOAuth.prototype.getAuthorizationHeader = function(url, method,
- opt_params) {
- var token = this.getToken();
- var secret = this.getTokenSecret();
- if (!token || !secret) {
- throw new Error("No oauth token or token secret");
- }
-
- var params = opt_params || {};
-
- return OAuthSimple().getHeaderString({
- action: method,
- path : url,
- parameters : params,
- signatures: {
- consumer_key : this.consumer_key,
- shared_secret : this.consumer_secret,
- oauth_secret : secret,
- oauth_token: token
- }
- });
-};
-
-/*******************************************************************************
- * PRIVATE API METHODS
- * Used by the library. There should be no need to call these methods directly.
- ******************************************************************************/
-
-/**
- * Creates a new ChromeExOAuth object from the supplied configuration object.
- * @param {Object} oauth_config Configuration parameters in a JavaScript object.
- * The following parameters are recognized:
- * "request_url" {String} OAuth request token URL.
- * "authorize_url" {String} OAuth authorize token URL.
- * "access_url" {String} OAuth access token URL.
- * "consumer_key" {String} OAuth consumer key.
- * "consumer_secret" {String} OAuth consumer secret.
- * "scope" {String} OAuth access scope.
- * "app_name" {String} Application name.
- * "auth_params" {Object} Additional parameters to pass to the
- * Authorization token URL. For an example, 'hd', 'hl', 'btmpl':
- * http://code.google.com/apis/accounts/docs/OAuth_ref.html#GetAuth
- * @return {ChromeExOAuth} An initialized ChromeExOAuth object.
- */
-ChromeExOAuth.fromConfig = function(oauth_config) {
- return new ChromeExOAuth(
- oauth_config['request_url'],
- oauth_config['authorize_url'],
- oauth_config['access_url'],
- oauth_config['consumer_key'],
- oauth_config['consumer_secret'],
- oauth_config['scope'],
- {
- 'app_name' : oauth_config['app_name'],
- 'auth_params' : oauth_config['auth_params']
- }
- );
-};
-
-/**
- * Initializes chrome_ex_oauth.html and redirects the page if needed to start
- * the OAuth flow. Once an access token is obtained, this function closes
- * chrome_ex_oauth.html.
- */
-ChromeExOAuth.initCallbackPage = function() {
- var background_page = chrome.extension.getBackgroundPage();
- var oauth_config = background_page.chromeExOAuthConfig;
- var oauth = ChromeExOAuth.fromConfig(oauth_config);
- background_page.chromeExOAuthRedirectStarted = true;
- oauth.initOAuthFlow(function (token, secret) {
- background_page.chromeExOAuthOnAuthorize(token, secret);
- background_page.chromeExOAuthRedirectStarted = false;
- chrome.tabs.getSelected(null, function (tab) {
- chrome.tabs.remove(tab.id);
- });
- });
-};
-
-/**
- * Sends an HTTP request. Convenience wrapper for XMLHttpRequest calls.
- * @param {String} method The HTTP method to use.
- * @param {String} url The URL to send the request to.
- * @param {Object} headers Optional request headers in key/value format.
- * @param {String} body Optional body content.
- * @param {Function} callback Function to call when the XMLHttpRequest's
- * ready state changes. See documentation for XMLHttpRequest's
- * onreadystatechange handler for more information.
- */
-ChromeExOAuth.sendRequest = function(method, url, headers, body, callback) {
- var xhr = new XMLHttpRequest();
- xhr.onreadystatechange = function(data) {
- callback(xhr, data);
- }
- xhr.open(method, url, true);
- if (headers) {
- for (var header in headers) {
- if (headers.hasOwnProperty(header)) {
- xhr.setRequestHeader(header, headers[header]);
- }
- }
- }
- xhr.send(body);
-};
-
-/**
- * Decodes a URL-encoded string into key/value pairs.
- * @param {String} encoded An URL-encoded string.
- * @return {Object} An object representing the decoded key/value pairs found
- * in the encoded string.
- */
-ChromeExOAuth.formDecode = function(encoded) {
- var params = encoded.split("&");
- var decoded = {};
- for (var i = 0, param; param = params[i]; i++) {
- var keyval = param.split("=");
- if (keyval.length == 2) {
- var key = ChromeExOAuth.fromRfc3986(keyval[0]);
- var val = ChromeExOAuth.fromRfc3986(keyval[1]);
- decoded[key] = val;
- }
- }
- return decoded;
-};
-
-/**
- * Returns the current window's querystring decoded into key/value pairs.
- * @return {Object} A object representing any key/value pairs found in the
- * current window's querystring.
- */
-ChromeExOAuth.getQueryStringParams = function() {
- var urlparts = window.location.href.split("?");
- if (urlparts.length >= 2) {
- var querystring = urlparts.slice(1).join("?");
- return ChromeExOAuth.formDecode(querystring);
- }
- return {};
-};
-
-/**
- * Binds a function call to a specific object. This function will also take
- * a variable number of additional arguments which will be prepended to the
- * arguments passed to the bound function when it is called.
- * @param {Function} func The function to bind.
- * @param {Object} obj The object to bind to the function's "this".
- * @return {Function} A closure that will call the bound function.
- */
-ChromeExOAuth.bind = function(func, obj) {
- var newargs = Array.prototype.slice.call(arguments).slice(2);
- return function() {
- var combinedargs = newargs.concat(Array.prototype.slice.call(arguments));
- func.apply(obj, combinedargs);
- };
-};
-
-/**
- * Encodes a value according to the RFC3986 specification.
- * @param {String} val The string to encode.
- */
-ChromeExOAuth.toRfc3986 = function(val){
- return encodeURIComponent(val)
- .replace(/\!/g, "%21")
- .replace(/\*/g, "%2A")
- .replace(/'/g, "%27")
- .replace(/\(/g, "%28")
- .replace(/\)/g, "%29");
-};
-
-/**
- * Decodes a string that has been encoded according to RFC3986.
- * @param {String} val The string to decode.
- */
-ChromeExOAuth.fromRfc3986 = function(val){
- var tmp = val
- .replace(/%21/g, "!")
- .replace(/%2A/g, "*")
- .replace(/%27/g, "'")
- .replace(/%28/g, "(")
- .replace(/%29/g, ")");
- return decodeURIComponent(tmp);
-};
-
-/**
- * Adds a key/value parameter to the supplied URL.
- * @param {String} url An URL which may or may not contain querystring values.
- * @param {String} key A key
- * @param {String} value A value
- * @return {String} The URL with URL-encoded versions of the key and value
- * appended, prefixing them with "&" or "?" as needed.
- */
-ChromeExOAuth.addURLParam = function(url, key, value) {
- var sep = (url.indexOf('?') >= 0) ? "&" : "?";
- return url + sep +
- ChromeExOAuth.toRfc3986(key) + "=" + ChromeExOAuth.toRfc3986(value);
-};
-
-/**
- * Stores an OAuth token for the configured scope.
- * @param {String} token The token to store.
- */
-ChromeExOAuth.prototype.setToken = function(token) {
- localStorage[this.key_token + encodeURI(this.oauth_scope)] = token;
-};
-
-/**
- * Retrieves any stored token for the configured scope.
- * @return {String} The stored token.
- */
-ChromeExOAuth.prototype.getToken = function() {
- return localStorage[this.key_token + encodeURI(this.oauth_scope)];
-};
-
-/**
- * Stores an OAuth token secret for the configured scope.
- * @param {String} secret The secret to store.
- */
-ChromeExOAuth.prototype.setTokenSecret = function(secret) {
- localStorage[this.key_token_secret + encodeURI(this.oauth_scope)] = secret;
-};
-
-/**
- * Retrieves any stored secret for the configured scope.
- * @return {String} The stored secret.
- */
-ChromeExOAuth.prototype.getTokenSecret = function() {
- return localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
-};
-
-/**
- * Starts an OAuth authorization flow for the current page. If a token exists,
- * no redirect is needed and the supplied callback is called immediately.
- * If this method detects that a redirect has finished, it grabs the
- * appropriate OAuth parameters from the URL and attempts to retrieve an
- * access token. If no token exists and no redirect has happened, then
- * an access token is requested and the page is ultimately redirected.
- * @param {Function} callback The function to call once the flow has finished.
- * This callback will be passed the following arguments:
- * token {String} The OAuth access token.
- * secret {String} The OAuth access token secret.
- */
-ChromeExOAuth.prototype.initOAuthFlow = function(callback) {
- if (!this.hasToken()) {
- var params = ChromeExOAuth.getQueryStringParams();
- if (params['chromeexoauthcallback'] == 'true') {
- var oauth_token = params['oauth_token'];
- var oauth_verifier = params['oauth_verifier']
- this.getAccessToken(oauth_token, oauth_verifier, callback);
- } else {
- var request_params = {
- 'url_callback_param' : 'chromeexoauthcallback'
- }
- this.getRequestToken(function(url) {
- window.location.href = url;
- }, request_params);
- }
- } else {
- callback(this.getToken(), this.getTokenSecret());
- }
-};
-
-/**
- * Requests an OAuth request token.
- * @param {Function} callback Function to call once the authorize URL is
- * calculated. This callback will be passed the following arguments:
- * url {String} The URL the user must be redirected to in order to
- * approve the token.
- * @param {Object} opt_args Optional arguments. The following parameters
- * are accepted:
- * "url_callback" {String} The URL the OAuth provider will redirect to.
- * "url_callback_param" {String} A parameter to include in the callback
- * URL in order to indicate to this library that a redirect has
- * taken place.
- */
-ChromeExOAuth.prototype.getRequestToken = function(callback, opt_args) {
- if (typeof callback !== "function") {
- throw new Error("Specified callback must be a function.");
- }
- var url = opt_args && opt_args['url_callback'] ||
- window && window.top && window.top.location &&
- window.top.location.href;
-
- var url_param = opt_args && opt_args['url_callback_param'] ||
- "chromeexoauthcallback";
- var url_callback = ChromeExOAuth.addURLParam(url, url_param, "true");
-
- var result = OAuthSimple().sign({
- path : this.url_request_token,
- parameters: {
- "xoauth_displayname" : this.app_name,
- "scope" : this.oauth_scope,
- "oauth_callback" : url_callback
- },
- signatures: {
- consumer_key : this.consumer_key,
- shared_secret : this.consumer_secret
- }
- });
- var onToken = ChromeExOAuth.bind(this.onRequestToken, this, callback);
- ChromeExOAuth.sendRequest("GET", result.signed_url, null, null, onToken);
-};
-
-/**
- * Called when a request token has been returned. Stores the request token
- * secret for later use and sends the authorization url to the supplied
- * callback (for redirecting the user).
- * @param {Function} callback Function to call once the authorize URL is
- * calculated. This callback will be passed the following arguments:
- * url {String} The URL the user must be redirected to in order to
- * approve the token.
- * @param {XMLHttpRequest} xhr The XMLHttpRequest object used to fetch the
- * request token.
- */
-ChromeExOAuth.prototype.onRequestToken = function(callback, xhr) {
- if (xhr.readyState == 4) {
- if (xhr.status == 200) {
- var params = ChromeExOAuth.formDecode(xhr.responseText);
- var token = params['oauth_token'];
- this.setTokenSecret(params['oauth_token_secret']);
- var url = ChromeExOAuth.addURLParam(this.url_auth_token,
- "oauth_token", token);
- for (var key in this.auth_params) {
- if (this.auth_params.hasOwnProperty(key)) {
- url = ChromeExOAuth.addURLParam(url, key, this.auth_params[key]);
- }
- }
- callback(url);
- } else {
- throw new Error("Fetching request token failed. Status " + xhr.status);
- }
- }
-};
-
-/**
- * Requests an OAuth access token.
- * @param {String} oauth_token The OAuth request token.
- * @param {String} oauth_verifier The OAuth token verifier.
- * @param {Function} callback The function to call once the token is obtained.
- * This callback will be passed the following arguments:
- * token {String} The OAuth access token.
- * secret {String} The OAuth access token secret.
- */
-ChromeExOAuth.prototype.getAccessToken = function(oauth_token, oauth_verifier,
- callback) {
- if (typeof callback !== "function") {
- throw new Error("Specified callback must be a function.");
- }
- var bg = chrome.extension.getBackgroundPage();
- if (bg.chromeExOAuthRequestingAccess == false) {
- bg.chromeExOAuthRequestingAccess = true;
-
- var result = OAuthSimple().sign({
- path : this.url_access_token,
- parameters: {
- "oauth_token" : oauth_token,
- "oauth_verifier" : oauth_verifier
- },
- signatures: {
- consumer_key : this.consumer_key,
- shared_secret : this.consumer_secret,
- oauth_secret : this.getTokenSecret(this.oauth_scope)
- }
- });
-
- var onToken = ChromeExOAuth.bind(this.onAccessToken, this, callback);
- ChromeExOAuth.sendRequest("GET", result.signed_url, null, null, onToken);
- }
-};
-
-/**
- * Called when an access token has been returned. Stores the access token and
- * access token secret for later use and sends them to the supplied callback.
- * @param {Function} callback The function to call once the token is obtained.
- * This callback will be passed the following arguments:
- * token {String} The OAuth access token.
- * secret {String} The OAuth access token secret.
- * @param {XMLHttpRequest} xhr The XMLHttpRequest object used to fetch the
- * access token.
- */
-ChromeExOAuth.prototype.onAccessToken = function(callback, xhr) {
- if (xhr.readyState == 4) {
- var bg = chrome.extension.getBackgroundPage();
- if (xhr.status == 200) {
- var params = ChromeExOAuth.formDecode(xhr.responseText);
- var token = params["oauth_token"];
- var secret = params["oauth_token_secret"];
- this.setToken(token);
- this.setTokenSecret(secret);
- bg.chromeExOAuthRequestingAccess = false;
- callback(token, secret);
- } else {
- bg.chromeExOAuthRequestingAccess = false;
- throw new Error("Fetching access token failed with status " + xhr.status);
- }
- }
-}; \ No newline at end of file
diff --git a/remoting/webapp/me2mom/chrome_ex_oauthsimple.js b/remoting/webapp/me2mom/chrome_ex_oauthsimple.js
deleted file mode 100644
index af0fe8a..0000000
--- a/remoting/webapp/me2mom/chrome_ex_oauthsimple.js
+++ /dev/null
@@ -1,458 +0,0 @@
-/* OAuthSimple
- * A simpler version of OAuth
- *
- * author: jr conlin
- * mail: src@anticipatr.com
- * copyright: unitedHeroes.net
- * version: 1.0
- * url: http://unitedHeroes.net/OAuthSimple
- *
- * Copyright (c) 2009, unitedHeroes.net
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the unitedHeroes.net nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY UNITEDHEROES.NET ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL UNITEDHEROES.NET BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-var OAuthSimple;
-
-if (OAuthSimple === undefined)
-{
- /* Simple OAuth
- *
- * This class only builds the OAuth elements, it does not do the actual
- * transmission or reception of the tokens. It does not validate elements
- * of the token. It is for client use only.
- *
- * api_key is the API key, also known as the OAuth consumer key
- * shared_secret is the shared secret (duh).
- *
- * Both the api_key and shared_secret are generally provided by the site
- * offering OAuth services. You need to specify them at object creation
- * because nobody <explative>ing uses OAuth without that minimal set of
- * signatures.
- *
- * If you want to use the higher order security that comes from the
- * OAuth token (sorry, I don't provide the functions to fetch that because
- * sites aren't horribly consistent about how they offer that), you need to
- * pass those in either with .setTokensAndSecrets() or as an argument to the
- * .sign() or .getHeaderString() functions.
- *
- * Example:
- <code>
- var oauthObject = OAuthSimple().sign({path:'http://example.com/rest/',
- parameters: 'foo=bar&gorp=banana',
- signatures:{
- api_key:'12345abcd',
- shared_secret:'xyz-5309'
- }});
- document.getElementById('someLink').href=oauthObject.signed_url;
- </code>
- *
- * that will sign as a "GET" using "SHA1-MAC" the url. If you need more than
- * that, read on, McDuff.
- */
-
- /** OAuthSimple creator
- *
- * Create an instance of OAuthSimple
- *
- * @param api_key {string} The API Key (sometimes referred to as the consumer key) This value is usually supplied by the site you wish to use.
- * @param shared_secret (string) The shared secret. This value is also usually provided by the site you wish to use.
- */
- OAuthSimple = function (consumer_key,shared_secret)
- {
-/* if (api_key == undefined)
- throw("Missing argument: api_key (oauth_consumer_key) for OAuthSimple. This is usually provided by the hosting site.");
- if (shared_secret == undefined)
- throw("Missing argument: shared_secret (shared secret) for OAuthSimple. This is usually provided by the hosting site.");
-*/ this._secrets={};
- this._parameters={};
-
- // General configuration options.
- if (consumer_key !== undefined) {
- this._secrets['consumer_key'] = consumer_key;
- }
- if (shared_secret !== undefined) {
- this._secrets['shared_secret'] = shared_secret;
- }
- this._default_signature_method= "HMAC-SHA1";
- this._action = "GET";
- this._nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-
-
- this.reset = function() {
- this._parameters={};
- this._path=undefined;
- return this;
- };
-
- /** set the parameters either from a hash or a string
- *
- * @param {string,object} List of parameters for the call, this can either be a URI string (e.g. "foo=bar&gorp=banana" or an object/hash)
- */
- this.setParameters = function (parameters) {
- if (parameters === undefined) {
- parameters = {};
- }
- if (typeof(parameters) == 'string') {
- parameters=this._parseParameterString(parameters);
- }
- this._parameters = parameters;
- if (this._parameters['oauth_nonce'] === undefined) {
- this._getNonce();
- }
- if (this._parameters['oauth_timestamp'] === undefined) {
- this._getTimestamp();
- }
- if (this._parameters['oauth_method'] === undefined) {
- this.setSignatureMethod();
- }
- if (this._parameters['oauth_consumer_key'] === undefined) {
- this._getApiKey();
- }
- if(this._parameters['oauth_token'] === undefined) {
- this._getAccessToken();
- }
-
- return this;
- };
-
- /** convienence method for setParameters
- *
- * @param parameters {string,object} See .setParameters
- */
- this.setQueryString = function (parameters) {
- return this.setParameters(parameters);
- };
-
- /** Set the target URL (does not include the parameters)
- *
- * @param path {string} the fully qualified URI (excluding query arguments) (e.g "http://example.org/foo")
- */
- this.setURL = function (path) {
- if (path == '') {
- throw ('No path specified for OAuthSimple.setURL');
- }
- this._path = path;
- return this;
- };
-
- /** convienence method for setURL
- *
- * @param path {string} see .setURL
- */
- this.setPath = function(path){
- return this.setURL(path);
- };
-
- /** set the "action" for the url, (e.g. GET,POST, DELETE, etc.)
- *
- * @param action {string} HTTP Action word.
- */
- this.setAction = function(action) {
- if (action === undefined) {
- action="GET";
- }
- action = action.toUpperCase();
- if (action.match('[^A-Z]')) {
- throw ('Invalid action specified for OAuthSimple.setAction');
- }
- this._action = action;
- return this;
- };
-
- /** set the signatures (as well as validate the ones you have)
- *
- * @param signatures {object} object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token: oauth_secret:}
- */
- this.setTokensAndSecrets = function(signatures) {
- if (signatures)
- {
- for (var i in signatures) {
- this._secrets[i] = signatures[i];
- }
- }
- // Aliases
- if (this._secrets['api_key']) {
- this._secrets.consumer_key = this._secrets.api_key;
- }
- if (this._secrets['access_token']) {
- this._secrets.oauth_token = this._secrets.access_token;
- }
- if (this._secrets['access_secret']) {
- this._secrets.oauth_secret = this._secrets.access_secret;
- }
- // Gauntlet
- if (this._secrets.consumer_key === undefined) {
- throw('Missing required consumer_key in OAuthSimple.setTokensAndSecrets');
- }
- if (this._secrets.shared_secret === undefined) {
- throw('Missing required shared_secret in OAuthSimple.setTokensAndSecrets');
- }
- if ((this._secrets.oauth_token !== undefined) && (this._secrets.oauth_secret === undefined)) {
- throw('Missing oauth_secret for supplied oauth_token in OAuthSimple.setTokensAndSecrets');
- }
- return this;
- };
-
- /** set the signature method (currently only Plaintext or SHA-MAC1)
- *
- * @param method {string} Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now)
- */
- this.setSignatureMethod = function(method) {
- if (method === undefined) {
- method = this._default_signature_method;
- }
- //TODO: accept things other than PlainText or SHA-MAC1
- if (method.toUpperCase().match(/(PLAINTEXT|HMAC-SHA1)/) === undefined) {
- throw ('Unknown signing method specified for OAuthSimple.setSignatureMethod');
- }
- this._parameters['oauth_signature_method']= method.toUpperCase();
- return this;
- };
-
- /** sign the request
- *
- * note: all arguments are optional, provided you've set them using the
- * other helper functions.
- *
- * @param args {object} hash of arguments for the call
- * {action:, path:, parameters:, method:, signatures:}
- * all arguments are optional.
- */
- this.sign = function (args) {
- if (args === undefined) {
- args = {};
- }
- // Set any given parameters
- if(args['action'] !== undefined) {
- this.setAction(args['action']);
- }
- if (args['path'] !== undefined) {
- this.setPath(args['path']);
- }
- if (args['method'] !== undefined) {
- this.setSignatureMethod(args['method']);
- }
- this.setTokensAndSecrets(args['signatures']);
- if (args['parameters'] !== undefined){
- this.setParameters(args['parameters']);
- }
- // check the parameters
- var normParams = this._normalizedParameters();
- this._parameters['oauth_signature']=this._generateSignature(normParams);
- return {
- parameters: this._parameters,
- signature: this._oauthEscape(this._parameters['oauth_signature']),
- signed_url: this._path + '?' + this._normalizedParameters(),
- header: this.getHeaderString()
- };
- };
-
- /** Return a formatted "header" string
- *
- * NOTE: This doesn't set the "Authorization: " prefix, which is required.
- * I don't set it because various set header functions prefer different
- * ways to do that.
- *
- * @param args {object} see .sign
- */
- this.getHeaderString = function(args) {
- if (this._parameters['oauth_signature'] === undefined) {
- this.sign(args);
- }
-
- var result = 'OAuth ';
- for (var pName in this._parameters)
- {
- if (!pName.match(/^oauth/)) {
- continue;
- }
- if ((this._parameters[pName]) instanceof Array)
- {
- var pLength = this._parameters[pName].length;
- for (var j=0;j<pLength;j++)
- {
- result += pName +'="'+this._oauthEscape(this._parameters[pName][j])+'" ';
- }
- }
- else
- {
- result += pName + '="'+this._oauthEscape(this._parameters[pName])+'" ';
- }
- }
- return result;
- };
-
- // Start Private Methods.
-
- /** convert the parameter string into a hash of objects.
- *
- */
- this._parseParameterString = function(paramString){
- var elements = paramString.split('&');
- var result={};
- for(var element=elements.shift();element;element=elements.shift())
- {
- var keyToken=element.split('=');
- var value='';
- if (keyToken[1]) {
- value=decodeURIComponent(keyToken[1]);
- }
- if(result[keyToken[0]]){
- if (!(result[keyToken[0]] instanceof Array))
- {
- result[keyToken[0]] = Array(result[keyToken[0]],value);
- }
- else
- {
- result[keyToken[0]].push(value);
- }
- }
- else
- {
- result[keyToken[0]]=value;
- }
- }
- return result;
- };
-
- this._oauthEscape = function(string) {
- if (string === undefined) {
- return "";
- }
- if (string instanceof Array)
- {
- throw('Array passed to _oauthEscape');
- }
- return encodeURIComponent(string).replace(/\!/g, "%21").
- replace(/\*/g, "%2A").
- replace(/'/g, "%27").
- replace(/\(/g, "%28").
- replace(/\)/g, "%29");
- };
-
- this._getNonce = function (length) {
- if (length === undefined) {
- length=5;
- }
- var result = "";
- var cLength = this._nonce_chars.length;
- for (var i = 0; i < length;i++) {
- var rnum = Math.floor(Math.random() *cLength);
- result += this._nonce_chars.substring(rnum,rnum+1);
- }
- this._parameters['oauth_nonce']=result;
- return result;
- };
-
- this._getApiKey = function() {
- if (this._secrets.consumer_key === undefined) {
- throw('No consumer_key set for OAuthSimple.');
- }
- this._parameters['oauth_consumer_key']=this._secrets.consumer_key;
- return this._parameters.oauth_consumer_key;
- };
-
- this._getAccessToken = function() {
- if (this._secrets['oauth_secret'] === undefined) {
- return '';
- }
- if (this._secrets['oauth_token'] === undefined) {
- throw('No oauth_token (access_token) set for OAuthSimple.');
- }
- this._parameters['oauth_token'] = this._secrets.oauth_token;
- return this._parameters.oauth_token;
- };
-
- this._getTimestamp = function() {
- var d = new Date();
- var ts = Math.floor(d.getTime()/1000);
- this._parameters['oauth_timestamp'] = ts;
- return ts;
- };
-
- this.b64_hmac_sha1 = function(k,d,_p,_z){
- // heavily optimized and compressed version of http://pajhome.org.uk/crypt/md5/sha1.js
- // _p = b64pad, _z = character size; not used here but I left them available just in case
- if(!_p){_p='=';}if(!_z){_z=8;}function _f(t,b,c,d){if(t<20){return(b&c)|((~b)&d);}if(t<40){return b^c^d;}if(t<60){return(b&c)|(b&d)|(c&d);}return b^c^d;}function _k(t){return(t<20)?1518500249:(t<40)?1859775393:(t<60)?-1894007588:-899497514;}function _s(x,y){var l=(x&0xFFFF)+(y&0xFFFF),m=(x>>16)+(y>>16)+(l>>16);return(m<<16)|(l&0xFFFF);}function _r(n,c){return(n<<c)|(n>>>(32-c));}function _c(x,l){x[l>>5]|=0x80<<(24-l%32);x[((l+64>>9)<<4)+15]=l;var w=[80],a=1732584193,b=-271733879,c=-1732584194,d=271733878,e=-1009589776;for(var i=0;i<x.length;i+=16){var o=a,p=b,q=c,r=d,s=e;for(var j=0;j<80;j++){if(j<16){w[j]=x[i+j];}else{w[j]=_r(w[j-3]^w[j-8]^w[j-14]^w[j-16],1);}var t=_s(_s(_r(a,5),_f(j,b,c,d)),_s(_s(e,w[j]),_k(j)));e=d;d=c;c=_r(b,30);b=a;a=t;}a=_s(a,o);b=_s(b,p);c=_s(c,q);d=_s(d,r);e=_s(e,s);}return[a,b,c,d,e];}function _b(s){var b=[],m=(1<<_z)-1;for(var i=0;i<s.length*_z;i+=_z){b[i>>5]|=(s.charCodeAt(i/8)&m)<<(32-_z-i%32);}return b;}function _h(k,d){var b=_b(k);if(b.length>16){b=_c(b,k.length*_z);}var p=[16],o=[16];for(var i=0;i<16;i++){p[i]=b[i]^0x36363636;o[i]=b[i]^0x5C5C5C5C;}var h=_c(p.concat(_b(d)),512+d.length*_z);return _c(o.concat(h),512+160);}function _n(b){var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s='';for(var i=0;i<b.length*4;i+=3){var r=(((b[i>>2]>>8*(3-i%4))&0xFF)<<16)|(((b[i+1>>2]>>8*(3-(i+1)%4))&0xFF)<<8)|((b[i+2>>2]>>8*(3-(i+2)%4))&0xFF);for(var j=0;j<4;j++){if(i*8+j*6>b.length*32){s+=_p;}else{s+=t.charAt((r>>6*(3-j))&0x3F);}}}return s;}function _x(k,d){return _n(_h(k,d));}return _x(k,d);
- }
-
-
- this._normalizedParameters = function() {
- var elements = new Array();
- var paramNames = [];
- var ra =0;
- for (var paramName in this._parameters)
- {
- if (ra++ > 1000) {
- throw('runaway 1');
- }
- paramNames.unshift(paramName);
- }
- paramNames = paramNames.sort();
- pLen = paramNames.length;
- for (var i=0;i<pLen; i++)
- {
- paramName=paramNames[i];
- //skip secrets.
- if (paramName.match(/\w+_secret/)) {
- continue;
- }
- if (this._parameters[paramName] instanceof Array)
- {
- var sorted = this._parameters[paramName].sort();
- var spLen = sorted.length;
- for (var j = 0;j<spLen;j++){
- if (ra++ > 1000) {
- throw('runaway 1');
- }
- elements.push(this._oauthEscape(paramName) + '=' +
- this._oauthEscape(sorted[j]));
- }
- continue;
- }
- elements.push(this._oauthEscape(paramName) + '=' +
- this._oauthEscape(this._parameters[paramName]));
- }
- return elements.join('&');
- };
-
- this._generateSignature = function() {
-
- var secretKey = this._oauthEscape(this._secrets.shared_secret)+'&'+
- this._oauthEscape(this._secrets.oauth_secret);
- if (this._parameters['oauth_signature_method'] == 'PLAINTEXT')
- {
- return secretKey;
- }
- if (this._parameters['oauth_signature_method'] == 'HMAC-SHA1')
- {
- var sigString = this._oauthEscape(this._action)+'&'+this._oauthEscape(this._path)+'&'+this._oauthEscape(this._normalizedParameters());
- return this.b64_hmac_sha1(secretKey,sigString);
- }
- return null;
- };
-
- return this;
- };
-}
diff --git a/remoting/webapp/me2mom/manifest.json b/remoting/webapp/me2mom/manifest.json
index 110f8dc..7b469ce4 100644
--- a/remoting/webapp/me2mom/manifest.json
+++ b/remoting/webapp/me2mom/manifest.json
@@ -13,6 +13,7 @@
},
"permissions": [
"tabs",
+ "https://accounts.google.com/o/oauth2/*",
"https://www.google.com/accounts/*",
"https://www.googleapis.com/chromoting/*"
],
diff --git a/remoting/webapp/me2mom/oauth2.js b/remoting/webapp/me2mom/oauth2.js
new file mode 100644
index 0000000..9b34129
--- /dev/null
+++ b/remoting/webapp/me2mom/oauth2.js
@@ -0,0 +1,138 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Declare an OAuth2 class to handle retrieval/storage of an OAuth2 token.
+//
+// Ideally, this should implement the OAuth2 PostMessage flow to avoid needing
+// to copy and paste a code, but that does not support extension URL schemes
+// quite yet. Instead, we currently use the native app flow with an
+// authorization code that the user must cut/paste.
+function OAuth2() {
+ this.OAUTH2_REFRESH_TOKEN_NAME = 'oauth2_refresh_token';
+
+ this.client_id = encodeURIComponent(
+ '440925447803-m890isgsr23kdkcu2erd4mirnrjalf98.' +
+ 'apps.googleusercontent.com');
+ this.client_secret = encodeURIComponent('TgKrL73H2kJe6Ir0ufp7bf6e');
+ this.scope = encodeURIComponent(
+ 'https://www.googleapis.com/auth/chromoting ' +
+ 'https://www.googleapis.com/auth/googletalk');
+ this.redirect_uri = encodeURIComponent('urn:ietf:wg:oauth:2.0:oob');
+}
+
+OAuth2.prototype.isAuthenticated = function() {
+ if(this.getRefreshToken()) {
+ return true;
+ }
+ return false;
+}
+
+OAuth2.prototype.clear = function() {
+ remoting.removeItem(this.OAUTH2_REFRESH_TOKEN_NAME);
+ delete this.access_token;
+ delete this.access_token_expiration;
+}
+
+OAuth2.prototype.setRefreshToken = function(token) {
+ remoting.setItem(this.OAUTH2_REFRESH_TOKEN_NAME, token);
+}
+
+OAuth2.prototype.getRefreshToken = function(token) {
+ return remoting.getItem(this.OAUTH2_REFRESH_TOKEN_NAME);
+}
+
+OAuth2.prototype.setAccessToken = function(token, expiration) {
+ this.access_token = token;
+ // Offset by 30 seconds to account for RTT issues.
+ // TODO(ajwong): See if this is necessary, or of the protocol already
+ // accounts for RTT.
+ this.access_token_expiration = expiration - 30000;
+}
+
+OAuth2.prototype.needsNewAccessToken = function() {
+ if (!this.isAuthenticated()) {
+ throw "Not Authenticated.";
+ }
+ if (!this.access_token) {
+ return true;
+ }
+ if (Date.now() > this.access_token_expiration) {
+ return true;
+ }
+ return false;
+}
+
+OAuth2.prototype.getAccessToken = function() {
+ if (this.needsNewAccessToken()) {
+ throw "Access Token expired.";
+ }
+ return this.access_token;
+}
+
+OAuth2.prototype.refreshAccessToken = function(on_done) {
+ if (!this.isAuthenticated()) {
+ throw "Not Authenticated.";
+ }
+ var xhr = new XMLHttpRequest();
+ var that = this;
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState != 4) {
+ return;
+ }
+ if (xhr.status == 200) {
+ tokens = JSON.parse(xhr.responseText);
+ that.setAccessToken(tokens['access_token'],
+ tokens['expires_in'] * 1000 + Date.now());
+ } else {
+ console.log("Refresh access token failed. Status: " + xhr.status +
+ " response: " + xhr.responseText);
+ }
+ on_done();
+ };
+ xhr.open('POST', 'https://accounts.google.com/o/oauth2/token', true);
+ xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+ var post_data = 'client_id=' + this.client_id
+ + '&client_secret=' + this.client_secret
+ + '&refresh_token=' + encodeURIComponent(this.getRefreshToken())
+ + '&grant_type=refresh_token';
+ xhr.send(post_data);
+}
+
+OAuth2.prototype.openOAuth2Window = function() {
+ var GET_CODE_URL = 'https://accounts.google.com/o/oauth2/auth?'
+ + 'client_id=' + this.client_id
+ + '&redirect_uri=' + this.redirect_uri
+ + '&scope=' + this.scope
+ + '&response_type=code';
+ window.open(GET_CODE_URL);
+}
+
+OAuth2.prototype.exchangeCodeForToken = function(code, on_done) {
+ var xhr = new XMLHttpRequest();
+ var that = this;
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState != 4) {
+ return;
+ }
+ if (xhr.status == 200) {
+ tokens = JSON.parse(xhr.responseText);
+ that.setRefreshToken(tokens['refresh_token']);
+ that.setAccessToken(tokens['access_token'],
+ tokens['expires_in'] + Date.now());
+ } else {
+ console.log("Code exchnage failed. Status: " + xhr.status +
+ " response: " + xhr.responseText);
+ }
+ on_done();
+ };
+ xhr.open('POST', 'https://accounts.google.com/o/oauth2/token', true);
+ xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+ var post_data = 'client_id=' + this.client_id
+ + '&client_secret=' + this.client_secret
+ + '&redirect_uri=' + this.redirect_uri
+ + '&code=' + encodeURIComponent(code)
+ + '&grant_type=authorization_code';
+ xhr.send(post_data);
+}
+
diff --git a/remoting/webapp/me2mom/remoting.js b/remoting/webapp/me2mom/remoting.js
index 2f364c11..656aa15 100644
--- a/remoting/webapp/me2mom/remoting.js
+++ b/remoting/webapp/me2mom/remoting.js
@@ -2,20 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// TODO(ajwong): This seems like a bad idea to share the exact same object
+// with the background page. Why are we doing it like this?
var remoting = chrome.extension.getBackgroundPage().remoting;
+
XMPP_LOGIN_NAME = 'xmpp_login';
XMPP_TOKEN_NAME = 'xmpp_token';
-OAUTH2_TOKEN_NAME = 'oauth2_token';
HOST_PLUGIN_ID = 'host_plugin_id';
function updateAuthStatus_() {
- var oauth1_status = document.getElementById('oauth1_status');
- if (remoting.oauth.hasToken()) {
- oauth1_status.innerText = 'OK';
- oauth1_status.style.color = 'green';
+ var oauth2_status = document.getElementById('oauth2_status');
+ if (remoting.oauth2.isAuthenticated()) {
+ oauth2_status.innerText = 'OK';
+ oauth2_status.style.color = 'green';
+ document.getElementById('oauth2_code_button').style.display = 'none';
+ document.getElementById('oauth2_clear_button').style.display = 'inline';
+ document.getElementById('oauth2_form').style.display = 'none';
} else {
- oauth1_status.innerText = 'Unauthorized';
- oauth1_status.style.color = 'red';
+ oauth2_status.innerText = 'Unauthorized';
+ oauth2_status.style.color = 'red';
+ document.getElementById('oauth2_code_button').style.display = 'inline';
+ document.getElementById('oauth2_clear_button').style.display = 'none';
+ document.getElementById('oauth2_form').style.display = 'inline';
}
var xmpp_status = document.getElementById('xmpp_status');
if (remoting.getItem(XMPP_TOKEN_NAME) && remoting.getItem(XMPP_LOGIN_NAME)) {
@@ -78,7 +86,7 @@ function initBackgroundFuncs_() {
remoting.getItem = chrome.extension.getBackgroundPage().getItem;
remoting.setItem = chrome.extension.getBackgroundPage().setItem;
remoting.removeItem = chrome.extension.getBackgroundPage().removeItem;
- remoting.oauth = chrome.extension.getBackgroundPage().oauth;
+ remoting.oauth2 = new OAuth2();
}
function authorizeXmpp(form) {
@@ -131,12 +139,12 @@ function authorizeXmpp(form) {
xhr.send(post_data);
}
-function authorizeOAuth1() {
- remoting.oauth.authorize(updateAuthStatus_);
+function authorizeOAuth2(code) {
+ remoting.oauth2.exchangeCodeForToken(code, updateAuthStatus_);
}
-function clearOAuth1() {
- remoting.oauth.clearTokens();
+function clearOAuth2() {
+ remoting.oauth2.clear();
updateAuthStatus_();
}
@@ -243,7 +251,7 @@ function showConnectError_(responseCode, responseString) {
setClientMode('connect_failed');
}
-function parseServerResponse_(reply, xhr) {
+function parseServerResponse_(xhr) {
if (xhr.status == 200) {
var host = JSON.parse(xhr.responseText);
if (host.data && host.data.jabberId) {
@@ -255,6 +263,24 @@ function parseServerResponse_(reply, xhr) {
showConnectError_(xhr.status, xhr.responseText);
}
+function resolveSupportId(support_id) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState != 4) {
+ return;
+ }
+ parseServerResponse_(xhr);
+ };
+
+ xhr.open('GET',
+ 'https://www.googleapis.com/chromoting/v1/support-hosts/' +
+ encodeURIComponent(support_id),
+ true);
+ xhr.setRequestHeader('Authorization',
+ 'OAuth ' + remoting.oauth2.getAccessToken());
+ xhr.send(null);
+}
+
function tryConnect(form) {
remoting.accessCode = form['access_code_entry'].value;
// TODO(jamiewalch): Since the mapping from (SupportId, HostSecret) to
@@ -264,10 +290,14 @@ function tryConnect(form) {
showConnectError_(404);
} else {
setClientMode('connecting');
- var urlBase = 'https://www.googleapis.com/chromoting/v1/support-hosts/';
- remoting.oauth.sendSignedRequest(
- urlBase + '' + encodeURIComponent(parts[0]) + '',
- parseServerResponse_);
+ if (remoting.oauth2.needsNewAccessToken()) {
+ remoting.oauth2.refreshAccessToken(function() {
+ resolveSupportId(parts[0]);
+ });
+ return;
+ } else {
+ resolveSupportId(parts[0]);
+ }
}
}