cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
z_dusatko
Community Participant

Grade Passback

Hello,

I am following Canvas Dev tutorial for LTI https://canvas.instructure.com/courses/785215/assignments/2233114?module_item_id=4761766 and I finally got to passing grade back from provider to Canvas. Now the signing process gets confusing, the tutorial says this quote:

"What you'll sent back to the learning platform is a POST request where the body is XML ( hey look, a nice little builder utility to get you started!) with a Content-Type header of of application/xml, signed using OAuth header signatures based on the same consumer key and shared secret you used to authorize the initial launch. Note: this is different than the way you received parameters from the learning platform since those all came across as POST multipart/form parameters, but you'll instead be sending auth information using the Authorization header, something along the lines of OAuth realm="http://sp.example.com/",oauth_consumer_key="0685bd9184jfhq22",oauth_token="ad180jjd733klru7",oauth_signature_method="HMAC-SHA1",oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",oauth_timestamp="137131200",oauth_nonce="4572616e48616d6d65724c61686176",oauth_version="1.0"."

Unfortunately it gives me 401 not authorized response with my configuration. I have a couple of questions:

1. Is it what follows OAuth just plain text with comma separated parameters?


2. If yes (according to OAuth Core 1.0 it looks like it is) do I always include empty oauth_token="" when I don't have one? 

3. Also do I include all parameters I get from LTI launch or just the above realm plus anything starting with "oauth_"?

4. In regards to signing XML POST body I am trying to do HMAC-SHA1 signature with LTI shared secret (I don't see consumer key requirement) and then adding it as another parameter 
oauth_body_hash="my HMAC-SHA1 signature" to above authorization header.
Is this correct? According to this draft OAuth Request Body Hash  it should be only SHA1 signature.

I am sorry for this long question. It just shows I might have a lot of misunderstanding. Also if anybody knows some node.js library for this whole thing it would awesome (I found only for HMAC signature).

Thanks for any advice,

Zbynek

Labels (1)
11 Replies
smithb_ccsd
Community Participant

Thanks Zbynek,

It appears that the code you provided is for verifying the oauth signature as opposed to handling the grade passback...?

Edit: Was able to get the Zbynek's code to work for verifying the Oauth signature by adjust the lti_launch_url.  I simply had to add a backslash to the end, like so: http://localhost:3000/.  Unfortunately, this does not answer my original question about how to get the grade passback to work.  The npm library that is suggested below is written in coffeescript and has too many open issues.

swolf
Community Participant

Hi Peter et al.,

 

First of all, thank you so much for all your great replies on this forum, they have helped me immensely! I'm a bit of beginner when it comes to coding, etc. I have a node server up and running using express, and a mongo database for my client secret and id. 

I'm building an app that allows students to do music exercises, then posts their scores back to Canvas. It works fine with my self-generated API token, but I've stumbled quite a bit with the OAuth flow. I finally have my local instance of Canvas accepting my app's launch via OAuth. I used the library you posted above ims-lti to verify the launch. The library also works to passback a test grade on launch (which is useless since the user hasn't done anything yet). But when it comes to actually persisting the provider instance I run into problems. 

My app loads a template with the exercise and the client-side javascript then sends the submission info back to my server via a post request. I need the provider instance to then send the grade info to Canvas from my node server. The problem is that the provider instance is gone, as is all the important launch data. I can save the launch data in a server-side session, I even tried storing the provider instance there, but I think the functionality of the provider instance is lost once it has been moved to req.session (maybe it's all just JSON by then?). provider.send_replace_result returns undefined if I try to use it...is there a suggested and safe way to save that provider instance while the server waits for the student to finish their assignment? 

Thanks in advance!

Scott 

UPDATE: I think I got it! I just instantiated a new lti.OutcomeService with the below options

const options = {
consumer_key: 1,
consumer_secret: req.session.provider.consumer_secret, /// LOOKUP FROM DATABASE???
service_url: req.session.provider.outcome_service.service_url,
source_did: req.session.provider.body.lis_result_sourcedid,
result_data_types: ['text']
};

const outcome = new lti.OutcomeService(options);

console.log(outcome.send_read_result);

const score = 95/100;
const message = `This Basics of Music assignment was completed with a score of ${score * 100}%.`;
outcome.send_read_result((err, previousScore) => {
if (err) {
console.log(err);
} else {
if (previousScore < score) {
outcome.send_replace_result_with_text(score, message, (err, result) => {
if (err) {
console.log(err);
} else {
console.log('sent grade with message');
}
})
}
}
})