The Instructure Community will enter a read-only state on November 22, 2025 as we prepare to migrate to our new Community platform in early December.
Read our blog post for more info about this change.
Found this content helpful? Log in or sign up to leave a like!
Hello, all
Ok, I am completely stumped. I have spent hours watching videos, following tutorials, tinkering with both my IIS server, the webconfig file on the IIS server, and my code itself. I have just been spinning my wheels so I come begging for help!
I made a webapp using HTML and Javascript making API calls via AJAX. Below is my Javascript code. What am I missing? Any help is greatly appreciated.
var API_Key = "MyAPIKEY";
function courseSearchID(){
var CourseID = document.getElementById("courseName").value;
console.log("Course Id:" + CourseID);
var available = {
"url": "https://[myDomain]/api/v1/courses/sis_course_id:" + CourseID +"?access_token="+API_Key,
"default":{
"dataType": "jsonp",
"type": "GET",
"contentType": "Application/json",
"crossDomain": true,
"Access-Control-Allow-Origin": "*",
}}
$.ajax(available).done(function (response) {
console.log(response);
var contentname = response.name;
var content = response.workflow_state;
$("#available").append("Course Name: ").append(contentname).append("<br>Status: ").append(content);
console.log("Course Availability");
})
Solved! Go to Solution.
When your browser attempts to make an XHR call across origins the browser will put additional checks on the response that is returned to make sure it should be allowed, this is using a standard called Cross Origin Resource Sharing (CORS). For the example you've given I think it's classed as a "Simple Request" in the CORS language and so there it's a pre-flight request, but when the response comes back the browser will check to see that it has Access-Control-Allow-Origin header and that it matches the origin that your page is being served from.
If you are attempting to make a request directly at a Canvas instance it will fail because by default Canvas doesn't return any Access-Control-Allow-Origin header in it's response, if you are talking to a self hosted instance then you could modify your Apache config to allow this (but be careful of the security implications). If you are talking to a Instructure hosted cloud instance then these headers can't be changed (as far as I'm aware).
In your code you can't the Access-Control-Allow-Origin to the request because you need it coming back on the response instead.
We've ended up building a proxy that manages OAuth tokens on behalf of other applications and then proxies requests to specific Canvas instances and adds the Access-Control-Allow-Origin headers on to the responses returned back. This allows a static HTML/JS/CSS frontend that has a signed JWT to make requests to a Canvas instance through the proxy.
The proxy that we built is only configured to work with our Canvas instance at the moment so you would need to have your own proxy to support this sort of development. So you'd need to write your own server-side proxy.
We are looking at open sourcing it to allow other people to also use it, but it's not yet publicly available in GitHub.
The endpoint it runs on is: https://proxy.canvas.ox.ac.uk/ but you can't do anything with it as you don't have any tools configured for it, the page you see there is just a holding page so our monitoring tools can check it's up, all the useful endpoints either require an LTI launch or a bearer header containing a JWT from a LTI launch.
When your browser attempts to make an XHR call across origins the browser will put additional checks on the response that is returned to make sure it should be allowed, this is using a standard called Cross Origin Resource Sharing (CORS). For the example you've given I think it's classed as a "Simple Request" in the CORS language and so there it's a pre-flight request, but when the response comes back the browser will check to see that it has Access-Control-Allow-Origin header and that it matches the origin that your page is being served from.
If you are attempting to make a request directly at a Canvas instance it will fail because by default Canvas doesn't return any Access-Control-Allow-Origin header in it's response, if you are talking to a self hosted instance then you could modify your Apache config to allow this (but be careful of the security implications). If you are talking to a Instructure hosted cloud instance then these headers can't be changed (as far as I'm aware).
In your code you can't the Access-Control-Allow-Origin to the request because you need it coming back on the response instead.
We've ended up building a proxy that manages OAuth tokens on behalf of other applications and then proxies requests to specific Canvas instances and adds the Access-Control-Allow-Origin headers on to the responses returned back. This allows a static HTML/JS/CSS frontend that has a signed JWT to make requests to a Canvas instance through the proxy.
So how exactly can I, if I can, access the proxy?
The proxy that we built is only configured to work with our Canvas instance at the moment so you would need to have your own proxy to support this sort of development. So you'd need to write your own server-side proxy.
We are looking at open sourcing it to allow other people to also use it, but it's not yet publicly available in GitHub.
The endpoint it runs on is: https://proxy.canvas.ox.ac.uk/ but you can't do anything with it as you don't have any tools configured for it, the page you see there is just a holding page so our monitoring tools can check it's up, all the useful endpoints either require an LTI launch or a bearer header containing a JWT from a LTI launch.
@matthew_buckett , thank you for the detail response. After posting this, I had spent a great deal of time reading about CORS (more than I want to admit) and came to the conclusion that I need to either sent my request through a proxy - which I don't want to do - or get our IT Canvas group to allow access to my app on their server - which wasn't going to happen.
For my needs right now, I have an extension that turns CORS policies off for when I'm making my requests (MOESIF CORS, if anyone is interested in the name of the extension). It looks like I'm the only one in my group that has shown interest in using it so I plan on re-writing the code to just make CURL requests.
Thanks again for your detailed response! It's the best response of all the articles I read - it was clear and concise.
You have to understand that the CORS behavior is not an error — it’s a mechanism that’s working as expected in order to protect your users, you, or the site you’re calling. If I understood it right you are doing an XMLHttpRequest to a different domain than your page is on. So the browser is blocking it as it usually allows a request in the same origin for security reasons. You need to do something different when you want to do a cross-domain request. In this case you need to enable your service for CORS which is cross origin resource sharing.
If you want to bypass that restriction when fetching the contents with fetch API or XMLHttpRequest in javascript, you can use a proxy server so that it sets the header Access-Control-Allow-Origin to *.
So, in other words, I would have to convince my organization to allow CORS on the server that is hosting Canvas for my application to call to it?
Hey bertscjn1 I'm so glad you asked this question! I went through a similar period of watching videos, studying others' shared solutions in GitHub, teaching myself as much as I could, meticulously following the documentation... yet no matter what I did, the Canvas API would only return a CORS error saying the Access-Control-Allow-Origin header is missing. I began to suspect the whole Canvas API was a big useless joke, or something, because it seemed impossible. Good thing I didn't give up hope! 😂
I'll explain my situation and what worked for me, in case it helps someone else.
So I have a java web application that is connected to our Canvas using LTI 1.3. Until now, I got everything I needed through the Custom Fields in our LTI Key under Additional Settings (i.e. as variable substitutions, documented here: https://canvas.instructure.com/doc/api/file.tools_variable_substitutions.html). However, my latest application requires a list of individual assignment submissions and grades. It didn't seem like I could get that from the LTI variable substitutions, and I thought, it really seems like I need a Canvas API call.
For my first attempt at making a Canvas API call, which failed, I tried accessing a test endpoint using javascript (ex: /api/vi/courses). I had hoped I could leverage the Oauth2 flow I had already configured for the LTI 1.3 setup, but after much fiddling I determined that wouldn't work. I concluded I'd have to replicate the Oauth2 flow all over again separately for the Canvas API. So in javascript, I carefully followed the Oauth2 flow described in the documentation (https://canvas.instructure.com/doc/api/file.oauth.html) and sent all my GET and POSTs using $.ajax() and fetch() calls. I followed Step 1, Redirect users to request Canvas access, and sucessfully received the "special code" back in my application's request_uri. However, I got stuck on Step 3. Whenever I tried to send the POST request with this code back to the specified URL. i.e. https://MYINSTITUTION.test.instructure.com/login/oauth2/token, it would give me the CORS error, saying the Access-Control-Allow-Origin header is missing. It didn't seem to matter that my application was already set up with a correct LTI 1.3 connection, and that I had a valid code: it didn't work, and I was stuck at step 3 like an impassable brick wall!
The solution that worked for me was is that I had to ditch the javascript approach, and instead, I realized I had to send my Canvas API calls from my application's server side. For Step 3, I ended up using Java Spring's WebClient (https://docs.spring.io/spring-framework/reference/web/webflux-webclient/client-body.html), like this:
//this is step 3, done in my application's server side java code
WebClient webClient = WebClient.create();
String myJSONresponse = webClient.post()
.uri("https://MYINSTITUTION.test.instructure.com/login/oauth2/token",
uri -> uri
//for an explanation of needed parameters, see: https://canvas.instructure.com/doc/api/file.oauth_endpoints.html#post-login-oauth2-token
.queryParam("grant_type", "authorization_code")
.queryParam("client_id", <MY_CLIENT_ID>)
.queryParam("client_secret", <MY_SECRET>)
.queryParam("redirect_uri", <URL_TO_MY_APPLICATION>)
.queryParam("code", <CODE_RECEIVED_FROM_STEP_2>)
.build())
.retrieve()
.bodyToMono(String.class)
.block();
//continue to parse result from JSON and do what you need...
To get the above client id, secret, etc. I had to create a new key in Canvas Admin, and this time it has to be a "API Key". So I don't know if this is correct, but, for my case I have both that as well as the "LTI Key" I already have. In the setup of the API Key, the "Redirect URIs" must match the above <URL_TO_MY_APPLICATION>. For <MY_CLIENT_ID>, it's what appears in the "Details" column, and, <MY_SECRET> is the long hidden string that appears when you press the "Show Key" button.
Anyway, the lesson I learned is that I have to make calls to the Canvas API from the server side of my LTI 1.3 application, and NOT from javascript!
Community helpTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign inTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign in