Canvas and Mastery are experiencing issues due to an ongoing AWS incident. Follow the status at AWS Health Dashboard and Instructure Status Page
Found this content helpful? Log in or sign up to leave a like!
I'm looking for someone that I can hire to walk me through the basics of building a basic LTI.
I've built a bunch of ideas for my school's own installation of Canvas. However, I would like to get to the point where I can share these ideas with the community and they can use them in their own installations.
Here's a few of the features I have built:
Maybe the first "Toy" LTI app that we could create together is getting this interactive triple beam balance that I created for my physics teacher to be an LTI that others could easily plop into their own lessons with a click of a button.
I work mostly with a Javascript/node/express stack if that matters. If you feel like this is something that you could help with, please feel free to message me.
Best Regards,
Jeremy Bell
Hi Jeremy,
There is a nice node.js module that does all the basic LTI heavy lifting: ims-lti - npm
Using this module its quite easy to get a basic LTI up quickly, so you might not need to hire someone in the first instance.
There is a really simple example running at the moment on node at https://lti.kno.nz:8443. You can add this as an LTI using Key/Secret: myschool.edu/letmein
If launched as a basic LTI It just shows the user's name and role. If you launch it as an external assignment it will also post a random score back to the gradebook. I'm happy to share the code if that would help you get started.
I'm wondering if you might be more interested in/need the ContentItem message. The ims-lti module also supports this, although I haven't used it.
Cheers,
Peter
Hi Peter thanks for the reply.
I installed the library, but anytime I run ".isValid" I get false returned. I'm kind of confused how the library sets up nonce validation etc.
If you could share the example code that would be great. Is it in a GitHub?
-Jeremy
Hi Jeremy,
Its not so big, so I''ve appended it below.
Please note that it doesn't worry about proper error handling or security.
And it was the first Node.js I ever wrote, so the code might not be the best!
Cheers,
Peter
const https = require('https');
const fs = require('fs');
const url = require('url');
var qs = require('querystring');
var options = {};
var sslkey = '/etc/letsencrypt/live/lti.kno.nz/privkey.pem';
if(fs.existsSync(sslkey)) {
options = {
key: fs.readFileSync(sslkey),
cert: fs.readFileSync('/etc/letsencrypt/live/lti.kno.nz/fullchain.pem')
}
}
else {
options = {
key: fs.readFileSync('./keys/star_netkno_nz.key'),
cert: fs.readFileSync('./keys/star_netkno_nz_bundle.crt')
}
};
var lti = require('ims-lti');
var ltiKey = 'myschool.edu';
var ltiSecret = 'letmein';
var b = {};
https.createServer(options, (req, res) => {
var q = url.parse(req.url, true);
if (req.method === 'POST') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
b = qs.parse(body);
console.log(b);
var provider = new lti.Provider(ltiKey, ltiSecret);
provider.valid_request(req, b, function (err, isValid){
if (err) {
console.log('Error in LTI Launch:' + err);
res.end('Pre-Validation Error');
}
else {
if (!isValid) {
console.log('\nError: Invalid LTI launch.');
res.end("Invalid LTI launch" );
}
else {
var rettxt = 'User: ' + b.lis_person_sourcedid + ' (' + b.lis_person_name_full + ')\n'
+ 'Roles: ' + b.roles + '\n';
// var retback = '<a href="' + b.launch_presentation_return_url + '">Return</a>';
var retback = '';
if (provider.outcome_service) {
// var score = Math.round( 100*Math.random() ) / 100;
var score = Math.round( 10*Math.random() ) / 10;
provider.outcome_service.send_replace_result(score, function(err, result) {
console.log(result);
});
res.end(rettxt + 'Returning score: ' + score + '\n\n' + retback);
}
else {
res.end(rettxt + 'No outcome service, no score return' + '\n\n' + retback);
}
}
}
});
});
}
else {
res.end('Must be launched as an LTI tool provider. Key/Secret: ' + ltiKey + '/' + ltiSecret);
}
}).listen(8443);
Hi Peter,
What does your console.log(b) return on line 39?
By the way how do you paste in formatted code like that?
What does your console.log(b) return on line 39?
{ oauth_consumer_key: 'myschool.edu',
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: '1530573322',
oauth_nonce: 'SyuUAnkMobFLjJxBG3kcIQFPF0wAnQxrRY1CyZ9GPJw',
oauth_version: '1.0',
context_id: 'f3e857e06acecd3237b45b3284a8b68df2331e26',
context_label: 'XML101',
context_title: 'XML101',
custom_canvas_assignment_points_possible: '0',
custom_canvas_assignment_title: 'Node Ass',
custom_canvas_enrollment_state: 'active',
ext_ims_lis_basic_outcome_url: 'http://192.168.1.104/api/lti/v1/tools/1/ext_grade_passback',
ext_lti_assignment_id: '3fd87cd3-de3c-44d3-ac11-f514f5b42568',
ext_outcome_data_values_accepted: 'url,text',
ext_outcome_result_total_score_accepted: 'true',
ext_outcome_submission_submitted_at_accepted: 'true',
ext_outcomes_tool_placement_url: 'http://192.168.1.104/api/lti/v1/turnitin/outcomes_placement/1',
ext_roles: 'urn:lti:instrole:ims/lis/Student,urn:lti:role:ims/lis/Learner,urn:lti:sysrole:ims/lis/User',
launch_presentation_document_target: 'iframe',
launch_presentation_locale: 'en-GB',
launch_presentation_return_url: 'http://192.168.1.104/courses/1/external_content/success/external_tool_redirect',
lis_outcome_service_url: 'http://192.168.1.104/api/lti/v1/tools/1/grade_passback',
lis_person_name_family: 'Testing',
lis_person_name_full: 'NK Testing',
lis_person_name_given: 'NK',
lis_result_sourcedid: '1-1-1-4-83cc7d22c23ca5693dd2a40c5c8d106eac6974f4',
lti_message_type: 'basic-lti-launch-request',
lti_version: 'LTI-1p0',
oauth_callback: 'about:blank',
resource_link_id: '7e49c7f77c241f716a712ae0a211feda6cbb518d',
resource_link_title: 'Node Ass',
roles: 'Learner',
tool_consumer_info_product_family_code: 'canvas',
tool_consumer_info_version: 'cloud',
tool_consumer_instance_contact_email: 'notifications@',
tool_consumer_instance_guid: '5635edb9b8b7b08fe3bec88e44fb0bba358ce16e.127.0.0.2',
tool_consumer_instance_name: 'korora',
user_id: '60d7f76ec3e2b16ab2f0aaa1f67603be4bedf1f9',
oauth_signature: '4eOtzDYfXWv2qGHckoUUx0nJyrA=' }
By the way how do you paste in formatted code like that?
Click the three dots (Expand toolbar), then More, then Syntax highlighter.
Here is the code that I tried in my app. I hard coded in the consumer key and secret just to simplify it.
Hi Jeremy,
I actually posted my code yesterday but it is undergoing moderation. Jive flags this automatically based on content, and then someone checks the post before releasing it (https://community.canvaslms.com/docs/DOC-14885-75187841192 ).
Hopefully it should be released soon.
Peter
You might be missing the body, I collect it explicitly and pass it in as a second argument:
provider.valid_request(req, body, function(err, isValid) {
When building an LTI I always use C#. There are several Nuget packages that people have created to make the process simpler. This is a sample of validating a user. I use the LtiLibrary.AspnetCore Nuget package by andyfmiller.
UCF has made a few Open Source LTI templates available for everyone at https://github.com/ucfopen so that you can jump right into LTI development.
We have templates available for Python, Ruby, and PHP.
Let me know if you have any questions about getting started with these.
To 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