home *** CD-ROM | disk | FTP | other *** search
/ Freelog 101 / FreelogNo101-JanvierFevrier2011.iso / Securite / Passter / Passter.exe / chrome_ex_oauth.js < prev    next >
Text File  |  2010-10-26  |  23KB  |  594 lines

  1. /**
  2.  * Copyright (c) 2010 The Chromium Authors. All rights reserved.  Use of this
  3.  * source code is governed by a BSD-style license that can be found in the
  4.  * LICENSE file.
  5.  */
  6.  
  7. /**
  8.  * Constructor - no need to invoke directly, call initBackgroundPage instead.
  9.  * @constructor
  10.  * @param {String} url_request_token The OAuth request token URL.
  11.  * @param {String} url_auth_token The OAuth authorize token URL.
  12.  * @param {String} url_access_token The OAuth access token URL.
  13.  * @param {String} consumer_key The OAuth consumer key.
  14.  * @param {String} consumer_secret The OAuth consumer secret.
  15.  * @param {String} oauth_scope The OAuth scope parameter.
  16.  * @param {Object} opt_args Optional arguments.  Recognized parameters:
  17.  *     "app_name" {String} Name of the current application
  18.  *     "callback_page" {String} If you renamed chrome_ex_oauth.html, the name
  19.  *          this file was renamed to.
  20.  */
  21. function ChromeExOAuth(url_request_token, url_auth_token, url_access_token,
  22.                        consumer_key, consumer_secret, oauth_scope, opt_args) {
  23.   this.url_request_token = url_request_token;
  24.   this.url_auth_token = url_auth_token;
  25.   this.url_access_token = url_access_token;
  26.   this.consumer_key = consumer_key;
  27.   this.consumer_secret = consumer_secret;
  28.   this.oauth_scope = oauth_scope;
  29.   this.app_name = opt_args && opt_args['app_name'] ||
  30.       "ChromeExOAuth Library";
  31.   this.key_token = "oauth_token";
  32.   this.key_token_secret = "oauth_token_secret";
  33.   this.callback_page = opt_args && opt_args['callback_page'] ||
  34.       "chrome_ex_oauth.html";
  35.   this.auth_params = {};
  36.   if (opt_args && opt_args['auth_params']) {
  37.     for (key in opt_args['auth_params']) {
  38.       if (opt_args['auth_params'].hasOwnProperty(key)) {
  39.         this.auth_params[key] = opt_args['auth_params'][key];
  40.       }
  41.     }
  42.   }
  43. };
  44.  
  45. /*******************************************************************************
  46.  * PUBLIC API METHODS
  47.  * Call these from your background page.
  48.  ******************************************************************************/
  49.  
  50. /**
  51.  * Initializes the OAuth helper from the background page.  You must call this
  52.  * before attempting to make any OAuth calls.
  53.  * @param {Object} oauth_config Configuration parameters in a JavaScript object.
  54.  *     The following parameters are recognized:
  55.  *         "request_url" {String} OAuth request token URL.
  56.  *         "authorize_url" {String} OAuth authorize token URL.
  57.  *         "access_url" {String} OAuth access token URL.
  58.  *         "consumer_key" {String} OAuth consumer key.
  59.  *         "consumer_secret" {String} OAuth consumer secret.
  60.  *         "scope" {String} OAuth access scope.
  61.  *         "app_name" {String} Application name.
  62.  *         "auth_params" {Object} Additional parameters to pass to the
  63.  *             Authorization token URL.  For an example, 'hd', 'hl', 'btmpl':
  64.  *             http://code.google.com/apis/accounts/docs/OAuth_ref.html#GetAuth
  65.  * @return {ChromeExOAuth} An initialized ChromeExOAuth object.
  66.  */
  67. ChromeExOAuth.initBackgroundPage = function(oauth_config) {
  68.   window.chromeExOAuthConfig = oauth_config;
  69.   window.chromeExOAuth = ChromeExOAuth.fromConfig(oauth_config);
  70.   window.chromeExOAuthRedirectStarted = false;
  71.   window.chromeExOAuthRequestingAccess = false;
  72.  
  73.   var url_match = chrome.extension.getURL(window.chromeExOAuth.callback_page);
  74.   var tabs = {};
  75.   chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
  76.     if (changeInfo.url &&
  77.         changeInfo.url.substr(0, url_match.length) === url_match &&
  78.         changeInfo.url != tabs[tabId] &&
  79.         window.chromeExOAuthRequestingAccess == false) {
  80.       chrome.tabs.create({ 'url' : changeInfo.url }, function(tab) {
  81.         tabs[tab.id] = tab.url;
  82.         chrome.tabs.remove(tabId);
  83.       });
  84.     }
  85.   });
  86.  
  87.   return window.chromeExOAuth;
  88. };
  89.  
  90. /**
  91.  * Authorizes the current user with the configued API.  You must call this
  92.  * before calling sendSignedRequest.
  93.  * @param {Function} callback A function to call once an access token has
  94.  *     been obtained.  This callback will be passed the following arguments:
  95.  *         token {String} The OAuth access token.
  96.  *         secret {String} The OAuth access token secret.
  97.  */
  98. ChromeExOAuth.prototype.authorize = function(callback) {
  99.   if (this.hasToken()) {
  100.     callback(this.getToken(), this.getTokenSecret());
  101.   } else {
  102.     window.chromeExOAuthOnAuthorize = function(token, secret) {
  103.       callback(token, secret);
  104.     };
  105.     chrome.tabs.create({ 'url' :chrome.extension.getURL(this.callback_page) });
  106.   }
  107. };
  108.  
  109. /**
  110.  * Clears any OAuth tokens stored for this configuration.  Effectively a
  111.  * "logout" of the configured OAuth API.
  112.  */
  113. ChromeExOAuth.prototype.clearTokens = function() {
  114.   delete localStorage[this.key_token + encodeURI(this.oauth_scope)];
  115.   delete localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
  116. };
  117.  
  118. /**
  119.  * Returns whether a token is currently stored for this configuration.
  120.  * Effectively a check to see whether the current user is "logged in" to
  121.  * the configured OAuth API.
  122.  * @return {Boolean} True if an access token exists.
  123.  */
  124. ChromeExOAuth.prototype.hasToken = function() {
  125.   return !!this.getToken();
  126. };
  127.  
  128. /**
  129.  * Makes an OAuth-signed HTTP request with the currently authorized tokens.
  130.  * @param {String} url The URL to send the request to.  Querystring parameters
  131.  *     should be omitted.
  132.  * @param {Function} callback A function to be called once the request is
  133.  *     completed.  This callback will be passed the following arguments:
  134.  *         responseText {String} The text response.
  135.  *         xhr {XMLHttpRequest} The XMLHttpRequest object which was used to
  136.  *             send the request.  Useful if you need to check response status
  137.  *             code, etc.
  138.  * @param {Object} opt_params Additional parameters to configure the request.
  139.  *     The following parameters are accepted:
  140.  *         "method" {String} The HTTP method to use.  Defaults to "GET".
  141.  *         "body" {String} A request body to send.  Defaults to null.
  142.  *         "parameters" {Object} Query parameters to include in the request.
  143.  *         "headers" {Object} Additional headers to include in the request.
  144.  */
  145. ChromeExOAuth.prototype.sendSignedRequest = function(url, callback,
  146.                                                      opt_params) {
  147.   var method = opt_params && opt_params['method'] || 'GET';
  148.   var body = opt_params && opt_params['body'] || null;
  149.   var params = opt_params && opt_params['parameters'] || {};
  150.   var headers = opt_params && opt_params['headers'] || {};
  151.  
  152.   var signedUrl = this.signURL(url, method, params);
  153.   //var authHeader = this.getAuthorizationHeader(url, method, params);
  154.   ChromeExOAuth.sendRequest(method, signedUrl, headers, body, function (xhr) {
  155.     if (xhr.readyState == 4) {
  156.       callback(xhr.responseText, xhr);
  157.     }
  158.   });
  159. };
  160.  
  161. /**
  162.  * Adds the required OAuth parameters to the given url and returns the
  163.  * result.  Useful if you need a signed url but don't want to make an XHR
  164.  * request.
  165.  * @param {String} method The http method to use.
  166.  * @param {String} url The base url of the resource you are querying.
  167.  * @param {Object} opt_params Query parameters to include in the request.
  168.  * @return {String} The base url plus any query params plus any OAuth params.
  169.  */
  170. ChromeExOAuth.prototype.signURL = function(url, method, opt_params) {
  171.   var token = this.getToken();
  172.   var secret = this.getTokenSecret();
  173.   if (!token || !secret) {
  174.     throw new Error("No oauth token or token secret");
  175.   }
  176.  
  177.   var params = opt_params || {};
  178.  
  179.   var result = OAuthSimple().sign({
  180.     action : method,
  181.     path : url,
  182.     parameters : params,
  183.     signatures: {
  184.       consumer_key : this.consumer_key,
  185.       shared_secret : this.consumer_secret,
  186.       oauth_secret : secret,
  187.       oauth_token: token
  188.     }
  189.   });
  190.   return result.signed_url;
  191. };
  192.  
  193. /**
  194.  * Generates the Authorization header based on the oauth parameters.
  195.  * @param {String} url The base url of the resource you are querying.
  196.  * @param {Object} opt_params Query parameters to include in the request.
  197.  * @return {String} An Authorization header containing the oauth_* params.
  198.  */
  199. ChromeExOAuth.prototype.getAuthorizationHeader = function(url, method,
  200.                                                           opt_params) {
  201.   var token = this.getToken();
  202.   var secret = this.getTokenSecret();
  203.   if (!token || !secret) {
  204.     throw new Error("No oauth token or token secret");
  205.   }
  206.  
  207.   var params = opt_params || {};
  208.  
  209.   return OAuthSimple().getHeaderString({
  210.     action: method,
  211.     path : url,
  212.     parameters : params,
  213.     signatures: {
  214.       consumer_key : this.consumer_key,
  215.       shared_secret : this.consumer_secret,
  216.       oauth_secret : secret,
  217.       oauth_token: token
  218.     }
  219.   });
  220. };
  221.  
  222. /*******************************************************************************
  223.  * PRIVATE API METHODS
  224.  * Used by the library.  There should be no need to call these methods directly.
  225.  ******************************************************************************/
  226.  
  227. /**
  228.  * Creates a new ChromeExOAuth object from the supplied configuration object.
  229.  * @param {Object} oauth_config Configuration parameters in a JavaScript object.
  230.  *     The following parameters are recognized:
  231.  *         "request_url" {String} OAuth request token URL.
  232.  *         "authorize_url" {String} OAuth authorize token URL.
  233.  *         "access_url" {String} OAuth access token URL.
  234.  *         "consumer_key" {String} OAuth consumer key.
  235.  *         "consumer_secret" {String} OAuth consumer secret.
  236.  *         "scope" {String} OAuth access scope.
  237.  *         "app_name" {String} Application name.
  238.  *         "auth_params" {Object} Additional parameters to pass to the
  239.  *             Authorization token URL.  For an example, 'hd', 'hl', 'btmpl':
  240.  *             http://code.google.com/apis/accounts/docs/OAuth_ref.html#GetAuth
  241.  * @return {ChromeExOAuth} An initialized ChromeExOAuth object.
  242.  */
  243. ChromeExOAuth.fromConfig = function(oauth_config) {
  244.   return new ChromeExOAuth(
  245.     oauth_config['request_url'],
  246.     oauth_config['authorize_url'],
  247.     oauth_config['access_url'],
  248.     oauth_config['consumer_key'],
  249.     oauth_config['consumer_secret'],
  250.     oauth_config['scope'],
  251.     {
  252.       'app_name' : oauth_config['app_name'],
  253.       'auth_params' : oauth_config['auth_params']
  254.     }
  255.   );
  256. };
  257.  
  258. /**
  259.  * Initializes chrome_ex_oauth.html and redirects the page if needed to start
  260.  * the OAuth flow.  Once an access token is obtained, this function closes
  261.  * chrome_ex_oauth.html.
  262.  */
  263. ChromeExOAuth.initCallbackPage = function() {
  264.   var background_page = chrome.extension.getBackgroundPage();
  265.   var oauth_config = background_page.chromeExOAuthConfig;
  266.   var oauth = ChromeExOAuth.fromConfig(oauth_config);
  267.   background_page.chromeExOAuthRedirectStarted = true;
  268.   oauth.initOAuthFlow(function (token, secret) {
  269.     background_page.chromeExOAuthOnAuthorize(token, secret);
  270.     background_page.chromeExOAuthRedirectStarted = false;
  271.     chrome.tabs.getSelected(null, function (tab) {
  272.       chrome.tabs.remove(tab.id);
  273.     });
  274.   });
  275. };
  276.  
  277. /**
  278.  * Sends an HTTP request.  Convenience wrapper for XMLHttpRequest calls.
  279.  * @param {String} method The HTTP method to use.
  280.  * @param {String} url The URL to send the request to.
  281.  * @param {Object} headers Optional request headers in key/value format.
  282.  * @param {String} body Optional body content.
  283.  * @param {Function} callback Function to call when the XMLHttpRequest's
  284.  *     ready state changes.  See documentation for XMLHttpRequest's
  285.  *     onreadystatechange handler for more information.
  286.  */
  287. ChromeExOAuth.sendRequest = function(method, url, headers, body, callback) {
  288.   var xhr = new XMLHttpRequest();
  289.   xhr.onreadystatechange = function(data) {
  290.     callback(xhr, data);
  291.   }
  292.   xhr.open(method, url, true);
  293.   if (headers) {
  294.     for (var header in headers) {
  295.       if (headers.hasOwnProperty(header)) {
  296.         xhr.setRequestHeader(header, headers[header]);
  297.       }
  298.     }
  299.   }
  300.   xhr.send(body);
  301. };
  302.  
  303. /**
  304.  * Decodes a URL-encoded string into key/value pairs.
  305.  * @param {String} encoded An URL-encoded string.
  306.  * @return {Object} An object representing the decoded key/value pairs found
  307.  *     in the encoded string.
  308.  */
  309. ChromeExOAuth.formDecode = function(encoded) {
  310.   var params = encoded.split("&");
  311.   var decoded = {};
  312.   for (var i = 0, param; param = params[i]; i++) {
  313.     var keyval = param.split("=");
  314.     if (keyval.length == 2) {
  315.       var key = ChromeExOAuth.fromRfc3986(keyval[0]);
  316.       var val = ChromeExOAuth.fromRfc3986(keyval[1]);
  317.       decoded[key] = val;
  318.     }
  319.   }
  320.   return decoded;
  321. };
  322.  
  323. /**
  324.  * Returns the current window's querystring decoded into key/value pairs.
  325.  * @return {Object} A object representing any key/value pairs found in the
  326.  *     current window's querystring.
  327.  */
  328. ChromeExOAuth.getQueryStringParams = function() {
  329.   var urlparts = window.location.href.split("?");
  330.   if (urlparts.length >= 2) {
  331.     var querystring = urlparts.slice(1).join("?");
  332.     return ChromeExOAuth.formDecode(querystring);
  333.   }
  334.   return {};
  335. };
  336.  
  337. /**
  338.  * Binds a function call to a specific object.  This function will also take
  339.  * a variable number of additional arguments which will be prepended to the
  340.  * arguments passed to the bound function when it is called.
  341.  * @param {Function} func The function to bind.
  342.  * @param {Object} obj The object to bind to the function's "this".
  343.  * @return {Function} A closure that will call the bound function.
  344.  */
  345. ChromeExOAuth.bind = function(func, obj) {
  346.   var newargs = Array.prototype.slice.call(arguments).slice(2);
  347.   return function() {
  348.     var combinedargs = newargs.concat(Array.prototype.slice.call(arguments));
  349.     func.apply(obj, combinedargs);
  350.   };
  351. };
  352.  
  353. /**
  354.  * Encodes a value according to the RFC3986 specification.
  355.  * @param {String} val The string to encode.
  356.  */
  357. ChromeExOAuth.toRfc3986 = function(val){
  358.    return encodeURIComponent(val)
  359.        .replace(/\!/g, "%21")
  360.        .replace(/\*/g, "%2A")
  361.        .replace(/'/g, "%27")
  362.        .replace(/\(/g, "%28")
  363.        .replace(/\)/g, "%29");
  364. };
  365.  
  366. /**
  367.  * Decodes a string that has been encoded according to RFC3986.
  368.  * @param {String} val The string to decode.
  369.  */
  370. ChromeExOAuth.fromRfc3986 = function(val){
  371.   var tmp = val
  372.       .replace(/%21/g, "!")
  373.       .replace(/%2A/g, "*")
  374.       .replace(/%27/g, "'")
  375.       .replace(/%28/g, "(")
  376.       .replace(/%29/g, ")");
  377.    return decodeURIComponent(tmp);
  378. };
  379.  
  380. /**
  381.  * Adds a key/value parameter to the supplied URL.
  382.  * @param {String} url An URL which may or may not contain querystring values.
  383.  * @param {String} key A key
  384.  * @param {String} value A value
  385.  * @return {String} The URL with URL-encoded versions of the key and value
  386.  *     appended, prefixing them with "&" or "?" as needed.
  387.  */
  388. ChromeExOAuth.addURLParam = function(url, key, value) {
  389.   var sep = (url.indexOf('?') >= 0) ? "&" : "?";
  390.   return url + sep +
  391.          ChromeExOAuth.toRfc3986(key) + "=" + ChromeExOAuth.toRfc3986(value);
  392. };
  393.  
  394. /**
  395.  * Stores an OAuth token for the configured scope.
  396.  * @param {String} token The token to store.
  397.  */
  398. ChromeExOAuth.prototype.setToken = function(token) {
  399.   localStorage[this.key_token + encodeURI(this.oauth_scope)] = token;
  400. };
  401.  
  402. /**
  403.  * Retrieves any stored token for the configured scope.
  404.  * @return {String} The stored token.
  405.  */
  406. ChromeExOAuth.prototype.getToken = function() {
  407.   return localStorage[this.key_token + encodeURI(this.oauth_scope)];
  408. };
  409.  
  410. /**
  411.  * Stores an OAuth token secret for the configured scope.
  412.  * @param {String} secret The secret to store.
  413.  */
  414. ChromeExOAuth.prototype.setTokenSecret = function(secret) {
  415.   localStorage[this.key_token_secret + encodeURI(this.oauth_scope)] = secret;
  416. };
  417.  
  418. /**
  419.  * Retrieves any stored secret for the configured scope.
  420.  * @return {String} The stored secret.
  421.  */
  422. ChromeExOAuth.prototype.getTokenSecret = function() {
  423.   return localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
  424. };
  425.  
  426. /**
  427.  * Starts an OAuth authorization flow for the current page.  If a token exists,
  428.  * no redirect is needed and the supplied callback is called immediately.
  429.  * If this method detects that a redirect has finished, it grabs the
  430.  * appropriate OAuth parameters from the URL and attempts to retrieve an
  431.  * access token.  If no token exists and no redirect has happened, then
  432.  * an access token is requested and the page is ultimately redirected.
  433.  * @param {Function} callback The function to call once the flow has finished.
  434.  *     This callback will be passed the following arguments:
  435.  *         token {String} The OAuth access token.
  436.  *         secret {String} The OAuth access token secret.
  437.  */
  438. ChromeExOAuth.prototype.initOAuthFlow = function(callback) {
  439.   if (!this.hasToken()) {
  440.     var params = ChromeExOAuth.getQueryStringParams();
  441.     if (params['chromeexoauthcallback'] == 'true') {
  442.       var oauth_token = params['oauth_token'];
  443.       var oauth_verifier = params['oauth_verifier']
  444.       this.getAccessToken(oauth_token, oauth_verifier, callback);
  445.     } else {
  446.       var request_params = {
  447.         'url_callback_param' : 'chromeexoauthcallback'
  448.       }
  449.       this.getRequestToken(function(url) {
  450.         window.location.href = url;
  451.       }, request_params);
  452.     }
  453.   } else {
  454.     callback(this.getToken(), this.getTokenSecret());
  455.   }
  456. };
  457.  
  458. /**
  459.  * Requests an OAuth request token.
  460.  * @param {Function} callback Function to call once the authorize URL is
  461.  *     calculated.  This callback will be passed the following arguments:
  462.  *         url {String} The URL the user must be redirected to in order to
  463.  *             approve the token.
  464.  * @param {Object} opt_args Optional arguments.  The following parameters
  465.  *     are accepted:
  466.  *         "url_callback" {String} The URL the OAuth provider will redirect to.
  467.  *         "url_callback_param" {String} A parameter to include in the callback
  468.  *             URL in order to indicate to this library that a redirect has
  469.  *             taken place.
  470.  */
  471. ChromeExOAuth.prototype.getRequestToken = function(callback, opt_args) {
  472.   if (typeof callback !== "function") {
  473.     throw new Error("Specified callback must be a function.");
  474.   }
  475.   var url = opt_args && opt_args['url_callback'] ||
  476.             window && window.top && window.top.location &&
  477.             window.top.location.href;
  478.  
  479.   var url_param = opt_args && opt_args['url_callback_param'] ||
  480.                   "chromeexoauthcallback";
  481.   var url_callback = ChromeExOAuth.addURLParam(url, url_param, "true");
  482.  
  483.   var result = OAuthSimple().sign({
  484.     path : this.url_request_token,
  485.     parameters: {
  486.       "xoauth_displayname" : this.app_name,
  487.       "scope" : this.oauth_scope,
  488.       "oauth_callback" : url_callback
  489.     },
  490.     signatures: {
  491.       consumer_key : this.consumer_key,
  492.       shared_secret : this.consumer_secret
  493.     }
  494.   });
  495.   var onToken = ChromeExOAuth.bind(this.onRequestToken, this, callback);
  496.   ChromeExOAuth.sendRequest("GET", result.signed_url, null, null, onToken);
  497. };
  498.  
  499. /**
  500.  * Called when a request token has been returned.  Stores the request token
  501.  * secret for later use and sends the authorization url to the supplied
  502.  * callback (for redirecting the user).
  503.  * @param {Function} callback Function to call once the authorize URL is
  504.  *     calculated.  This callback will be passed the following arguments:
  505.  *         url {String} The URL the user must be redirected to in order to
  506.  *             approve the token.
  507.  * @param {XMLHttpRequest} xhr The XMLHttpRequest object used to fetch the
  508.  *     request token.
  509.  */
  510. ChromeExOAuth.prototype.onRequestToken = function(callback, xhr) {
  511.   if (xhr.readyState == 4) {
  512.     if (xhr.status == 200) {
  513.       var params = ChromeExOAuth.formDecode(xhr.responseText);
  514.       var token = params['oauth_token'];
  515.       this.setTokenSecret(params['oauth_token_secret']);
  516.       var url = ChromeExOAuth.addURLParam(this.url_auth_token,
  517.                                           "oauth_token", token);
  518.       for (var key in this.auth_params) {
  519.         if (this.auth_params.hasOwnProperty(key)) {
  520.           url = ChromeExOAuth.addURLParam(url, key, this.auth_params[key]);
  521.         }
  522.       }
  523.       callback(url);
  524.     } else {
  525.       throw new Error("Fetching request token failed. Status " + xhr.status);
  526.     }
  527.   }
  528. };
  529.  
  530. /**
  531.  * Requests an OAuth access token.
  532.  * @param {String} oauth_token The OAuth request token.
  533.  * @param {String} oauth_verifier The OAuth token verifier.
  534.  * @param {Function} callback The function to call once the token is obtained.
  535.  *     This callback will be passed the following arguments:
  536.  *         token {String} The OAuth access token.
  537.  *         secret {String} The OAuth access token secret.
  538.  */
  539. ChromeExOAuth.prototype.getAccessToken = function(oauth_token, oauth_verifier,
  540.                                                   callback) {
  541.   if (typeof callback !== "function") {
  542.     throw new Error("Specified callback must be a function.");
  543.   }
  544.   var bg = chrome.extension.getBackgroundPage();
  545.   if (bg.chromeExOAuthRequestingAccess == false) {
  546.     bg.chromeExOAuthRequestingAccess = true;
  547.  
  548.     var result = OAuthSimple().sign({
  549.       path : this.url_access_token,
  550.       parameters: {
  551.         "oauth_token" : oauth_token,
  552.         "oauth_verifier" : oauth_verifier
  553.       },
  554.       signatures: {
  555.         consumer_key : this.consumer_key,
  556.         shared_secret : this.consumer_secret,
  557.         oauth_secret : this.getTokenSecret(this.oauth_scope)
  558.       }
  559.     });
  560.  
  561.     var onToken = ChromeExOAuth.bind(this.onAccessToken, this, callback);
  562.     ChromeExOAuth.sendRequest("GET", result.signed_url, null, null, onToken);
  563.   }
  564. };
  565.  
  566. /**
  567.  * Called when an access token has been returned.  Stores the access token and
  568.  * access token secret for later use and sends them to the supplied callback.
  569.  * @param {Function} callback The function to call once the token is obtained.
  570.  *     This callback will be passed the following arguments:
  571.  *         token {String} The OAuth access token.
  572.  *         secret {String} The OAuth access token secret.
  573.  * @param {XMLHttpRequest} xhr The XMLHttpRequest object used to fetch the
  574.  *     access token.
  575.  */
  576. ChromeExOAuth.prototype.onAccessToken = function(callback, xhr) {
  577.   if (xhr.readyState == 4) {
  578.     var bg = chrome.extension.getBackgroundPage();
  579.     if (xhr.status == 200) {
  580.       var params = ChromeExOAuth.formDecode(xhr.responseText);
  581.       var token = params["oauth_token"];
  582.       var secret = params["oauth_token_secret"];
  583.       this.setToken(token);
  584.       this.setTokenSecret(secret);
  585.       bg.chromeExOAuthRequestingAccess = false;
  586.       callback(token, secret);
  587.     } else {
  588.       bg.chromeExOAuthRequestingAccess = false;
  589.       throw new Error("Fetching access token failed with status " + xhr.status);
  590.     }
  591.   }
  592. };
  593.  
  594.