0

Note: I do not have enough reputation to post all the links, so I just removed the first letter so that the text editor would not read it as a link. To go to them please just paste it into your address bar and add an h in the front.

I am trying to access user data from Khan Academy using their API, however, this is the first time I have used their API or AJAX. So far I have managed to retrieve data on videos or exercises, but I am having trouble with using OAuth to retrieve user data. Here is info on Khan Academy authorization. They use OAuth 1.0. Here is the OAuth library I am using. Here is the documentation for the KA API: http://api-explorer.khanacademy.org/ I have tried:

var oauth = OAuth({
  consumer: {
    public: '****************',
    secret: '****************'
  },
  signature_method: 'HMAC-SHA1'
});

var request_data = {
  url: 'http://www.khanacademy.org/api/v1/exercises/logarithms_1',
  method: 'GET'
};

$.ajax({
  url: request_data.url,
  type: request_data.method,
  headers: oauth.toHeader(oauth.authorize(request_data))
}).done(function(data) {
  alert("done");
});
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
  <title>KA API Test</title>
  <meta http-equiv="Access-Control-Allow-Origin" content="*">
  <meta http-equiv="content-type" content="text/html;charset=utf-8" />
  <meta name="generator" content="Geany 1.23.1" />
  <!-- sha1 -->
  <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js"></script>
  <!-- sha256 -->
  <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha256.js"></script>

  <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>
  <script src="oauth-1.0a.js"></script>
  <script src="jquery-1.12.0.min.js"></script>
</head>

<body>
</body>

</html>

I have also tried this Javascript with the same HTML:

var oauth = OAuth({
  consumer: {
    public: 'qdMdMjjJQKrwJw2S',
    secret: '7XeknfpVBzx8fGMK'
  },
  signature_method: 'HMAC-SHA1'
});

var request_data = {
  url: 'http://www.khanacademy.org/api/v1/exercises/logarithms_1',
  method: 'GET'
};

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.khanacademy.org/api/v1/exercises/logarithms_1", true);
xhr.setRequestHeader("Authorization", oauth.toHeader(oauth.authorize(request_data)));
xhr.send();
xhr.addEventListener("readystatechange", processRequest, false);

function processRequest(e) {
  if (xhr.readyState == 4 && xhr.status == 200) {
    var response = JSON.parse(xhr.responseText);
    for (var key in response) {
      console.log(key + ":" + response[key]);
    }
    alert(response.translated_description_html);
  }
}

Both give this error in the console: "XMLHttpRequest cannot load http://www.khanacademy.org/api/v1/exercises/logarithms_1. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access."

Note: The XMLHttpRequest works fine and returns what I want before adding xhr.setRequestHeader("Authorization", "OAuth " + oauth.toHeader(oauth.authorize(request_data))); to it. However, that is just a test link which does not require authorization and I will change it later.

P.S. I would like to fix this error, but I also just need to use OAuth correctly and I think they are related because I did not have the error before using OAuth.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Joshua R C
  • 73
  • 1
  • 7

1 Answers1

0

The problem is with Cross Domain Access Origins. You need to ask the API Owner to allow Cross Domains Origin.

Access-Control-Allow-Origin is a CORS (Cross-Origin Resource Sharing) header.

When Site A tries to fetch content from Site B, Site B can send an Access-Control-Allow-Origin response header to tell the browser that the content of this page is accessible to certain origins. (An origin is a domain, plus a scheme and port number.) By default, Site B's pages are not accessible to any other origin; using the Access-Control-Allow-Origin header opens a door for cross-origin access by specific requesting origins.

For each resource/page that Site B wants to make accessible to Site A, Site B should serve its pages with the response header:

Access-Control-Allow-Origin: http:// siteA.com Modern browsers will not block cross-domain requests outright. If Site A requests a page from Site B, the browser will actually fetch the requested page on the network level and check if the response headers list Site A as a permitted requester domain. If Site B has not indicated that Site A is allowed to access this page, the browser will trigger the XMLHttpRequest's error event and deny the response data to the requesting JavaScript code.

Non-simple requests What happens on the network level can be slightly more complex than explained above. If the request is a "non-simple" request, the browser first sends a data-less "preflight" OPTIONS request, to verify that the server will accept the request. A request is non-simple when either (or both):

using an HTTP verb other than GET or POST (e.g. PUT, DELETE) using non-simple request headers; the only simple requests headers are: Accept Accept-Language Content-Language Content-Type (this is only simple when its value is application/x-www-form-urlencoded, multipart/form-data, or text/plain) If the server responds to the OPTIONS preflight with appropriate response headers (Access-Control-Allow-Headers for non-simple headers, Access-Control-Allow-Methods for non-simple verbs) that match the non-simple verb and/or non-simple headers, then the browser sends the actual request.

Supposing that Site A wants to send a PUT request for /somePage, with a non-simple Content-Type value of application/json, the browser would first send a preflight request:

OPTIONS /somePage HTTP/1.1 Origin: http://siteA.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: Content-Type Note that Access-Control-Request-Method and Access-Control-Request-Headers are added by the browser automatically; you do not need to add them. This OPTIONS preflight gets the successful response headers:

Access-Control-Allow-Origin: http://  siteA.com

Access-Control-Allow-Methods: GET, POST, PUT

Access-Control-Allow-Headers: Content-Type

When sending the actual request (after preflight is done), the behavior is identical to how a simple request is handled. In other words, a non-simple request whose preflight is successful is treated the same as a simple request (i.e., the server must still send Access-Control-Allow-Origin again for the actual response).

The browsers sends the actual request:

PUT /somePage HTTP/1.1 Origin: http:// siteA.com Content-Type: application/json

{ "myRequestContent": "Response in JSON" } And the server sends back an Access-Control-Allow-Origin, just as it would for a simple request:

Access-Control-Allow-Origin: http:// siteA.com See Understanding XMLHttpRequest over CORS for a little more information about non-simple requests.

  • and to allow every where you can use: `Access-Control-Allow-Origin: * ` as wildcard entry – Kunwar Adeel Apr 03 '16 at 05:13
  • You can learn more from the below url: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS – Kunwar Adeel Apr 03 '16 at 05:16
  • So if the problem is that Cross Origin Requests are not allowed by Khan Academy, then why does the program work before adding authentication. The problem must be somewhere with the way I am using or the way Khan Academy has set up OAuth (most likely the former). – Joshua R C Apr 04 '16 at 22:00
  • The simple solution to avoid cross-browser requests is to redirect the user to Khan's OAuth page with a proper redirect URL back to your site. The problem is that you're trying to do it using AJAX. – thomas88wp Jun 03 '16 at 18:52
  • (Should say cross-*origin* above.) I should add that once you've authenticated and gotten the user's oauth_verifier token, you can use AJAX. To avoid the cross-origin problem, you use [JSONP](https://en.wikipedia.org/wiki/JSONP), as described at the bottom of the Khan [docs](https://github.com/Khan/khan-api/wiki/Khan-Academy-API). – thomas88wp Jun 03 '16 at 18:57