I am developing an LTI Tool Provider in Python and have had no problems getting any aspects of LTI 1.x basic launch working or the Content Item specification. I have hit a huge brick wall though trying to use the LTI 2 registration mechanism.
Whenever I run my code on some Tool Consumers all I get is a 400 error when I post my tool proxy to the tool proxy endpoint listed in the tool consumer profile.
I have tried this on:
Unfortunately nothing is giving me any idea of what is bad about the request. I am 90% certain that it is the JSON or something in the headers. I am pretty sure it is not the oAuth (Thanks to Comments below and the fact that it works in 2 of the 4 tested environments)
Supposedly Moodle gives you some output somewhere, but I have looked everywhere and haven't found it. I turned on the development mode from the Site Administrator Menu and followed several directions on how to make sure that errors are being logged in PHP and I can't find anything anywhere.
Originally I couldn't get this to work anywhere, but I was able to hook up xdebug to the local Moodle 3.3 instance and that's how I was finally able to get that working.
Here is the Tool Consumer Profile from Canvas:
{'@context':
['http://purl.imsglobal.org/ctx/lti/v2/ToolConsumerProfile'],
'@id': 'https://canvas.instructure.com/api/lti/courses/1146163/tool_consumer_profile/339b6700-e4cb-47c5-a54f-3ee0064921a9',
'@type': 'ToolConsumerProfile',
'capability_offered': ['basic-lti-launch-request',
'User.id',
'Canvas.api.domain',
'LtiLink.custom.url',
'ToolProxyBinding.custom.url',
'ToolProxy.custom.url',
'Canvas.placements.accountNavigation',
'Canvas.placements.courseNavigation',
'Canvas.placements.assignmentSelection',
'Canvas.placements.linkSelection',
'Canvas.placements.postGrades',
'User.username',
'Person.email.primary',
'vnd.Canvas.Person.email.sis',
'Person.name.given',
'Person.name.family',
'Person.name.full',
'CourseSection.sourcedId',
'Person.sourcedId',
'Membership.role',
'ToolConsumerProfile.url',
'Security.splitSecret',
'Context.id',
'ToolConsumerInstance.guid',
'CourseSection.sourcedId',
'Membership.role',
'Person.email.primary',
'Person.name.given',
'Person.name.family',
'Person.name.full',
'Person.sourcedId',
'User.id',
'User.image',
'Message.documentTarget',
'Message.locale',
'Context.id',
'vnd.Canvas.root_account.uuid'],
'guid': '339b6700-e4cb-47c5-a54f-3ee0064921a9',
'lti_version': 'LTI-2p0',
'product_instance': {'guid': '07adb3e60637ff02d9ea11c7c74f1ca921699bd7.canvas.instructure.com',
'product_info': {'product_family': {'code': 'canvas',
'vendor': {'code': 'https://instructure.com',
'timestamp': '2008-03-27T06:00:00Z',
'vendor_name': {'default_value': 'Instructure',
'key': 'vendor.name'}}},
'product_name': {'default_value': 'Canvas '
'by '
'Instructure',
'key': 'product.name'},
'product_version': 'none'},
'service_owner': {'description': {'default_value': 'Free '
'For '
'Teachers',
'key': 'service_owner.description'},
'service_owner_name': {'default_value': 'Free '
'For '
'Teachers',
'key': 'service_owner.name'}}},
'security_profile': [{'digest_algorithm': 'HMAC-SHA1',
'security_profile_name': 'lti_oauth_hash_message_security'},
{'digest_algorithm': 'HS256',
'security_profile_name': 'oauth2_access_token_ws_security'}],
'service_offered': [{'@id': 'https://canvas.instructure.com/api/lti/courses/1146163/tool_consumer_profile/339b6700-e4cb-47c5-a54f-3ee0064921a9#ToolProxy.collection',
'@type': 'RestService',
'action': ['POST'],
'endpoint': 'https://canvas.instructure.com/api/lti/courses/1146163/tool_proxy',
'format': ['application/vnd.ims.lti.v2.toolproxy+json']},
{'@id': 'https://canvas.instructure.com/api/lti/courses/1146163/tool_consumer_profile/339b6700-e4cb-47c5-a54f-3ee0064921a9#ToolProxy.item',
'@type': 'RestService',
'action': ['GET'],
'endpoint': 'https://canvas.instructure.com/api/lti/tool_proxy/{tool_proxy_guid}',
'format': ['application/vnd.ims.lti.v2.toolproxy+json']},
{'@id': 'https://canvas.instructure.com/api/lti/courses/1146163/tool_consumer_profile/339b6700-e4cb-47c5-a54f-3ee0064921a9#vnd.Canvas.authorization',
'@type': 'RestService',
'action': ['POST'],
'endpoint': 'https://canvas.instructure.com/api/lti/courses/1146163/authorize',
'format': ['application/json']},
{'@id': 'https://canvas.instructure.com/api/lti/courses/1146163/tool_consumer_profile/339b6700-e4cb-47c5-a54f-3ee0064921a9#ToolProxySettings',
'@type': 'RestService',
'action': ['GET', 'PUT'],
'endpoint': 'https://canvas.instructure.com/api/lti/tool_settings/tool_proxy/{tool_proxy_id}',
'format': ['application/vnd.ims.lti.v2.toolsettings+json',
'application/vnd.ims.lti.v2.toolsettings.simple+json']},
{'@id': 'https://canvas.instructure.com/api/lti/courses/1146163/tool_consumer_profile/339b6700-e4cb-47c5-a54f-3ee0064921a9#ToolProxyBindingSettings',
'@type': 'RestService',
'action': ['GET', 'PUT'],
'endpoint': 'https://canvas.instructure.com/api/lti/tool_settings/bindings/{binding_id}',
'format': ["application/vnd.ims.lti.v2.toolsettings+json'",
'application/vnd.ims.lti.v2.toolsettings.simple+json']},
{'@id': 'https://canvas.instructure.com/api/lti/courses/1146163/tool_consumer_profile/339b6700-e4cb-47c5-a54f-3ee0064921a9#LtiLinkSettings',
'@type': 'RestService',
'action': ['GET', 'PUT'],
'endpoint': 'https://canvas.instructure.com/api/lti/tool_settings/links/{tool_proxy_id}',
'format': ['application/vnd.ims.lti.v2.toolsettings+json',
'application/vnd.ims.lti.v2.toolsettings.simple+json']}]}
And Here is the Tool Proxy I am sending back (I tried to pull out anything that seemed optional.)
POST https://canvas.instructure.com/api/lti/courses/1146163/tool_proxy
{
"@type": "ToolProxy",
"@context": "http://purl.imsglobal.org/ctx/lti/v2/ToolProxy",
"tool_proxy_guid": "339b6700-e4cb-47c5-a54f-3ee0064921a9",
"tool_consumer_profile": "https://canvas.instructure.com/api/lti/courses/1146163/tool_consumer_profile",
"tool_profile": {
"base_url_choice": [
{
"default_base_url": "http://localhost:9090/",
"secure_base_url": "http://localhost:9090/",
"selector": {
"applies_to": [
"IconEndpoint",
"MessageHandler"
]
}
}
],
"lti_version": "LTI-2p0",
"product_instance": {
"guid": "1431963455",
"service_owner": {
"service_owner_name": {
"key": "service_owner.name",
"default_value": "EHR Tutor"
},
"description": {
"key": "service_owner.description",
"default_value": "Provider of high quality education"
},
"timestamp": "2017-05-04T05:37:35-05:00"
},
"product_info": {
"product_name": {
structure.com/api/lti/courses/1146163/tool_proxy/339b6700-e4cb-47c5-a54f-3ee0064921a9"\n}'
"key": "tool.name",
"default_value": "EHR Tutor"
},
"description": {
"key": "tool.description",
"default_value": "EHR Tutor"
},
"product_family": {
"vendor": {
"description": {
"key": "tool.vendor.description",
"default_value": "Noggin LLC"
},
"contact": {
"email": "info@ehrtutor.com"
},
"code": "ehrtutor.com",
"timestamp": "2017-05-04T05:37:35-05:00",
"website": "https://www.ehrtutor.com",
"vendor_name": {
"key": "tool.vendor.name",
"default_value": "Noggin LLC"
}
},
"code": "assessment-tool",
"@id": "https://my.ehrtutor.com"
},
"product_version": "0.0.1b",
"technical_description": {
"key": "tool.technical",
"default_value": "Support provided for LTI 2"
}
},
"support": {
"email": "support@ehrtutor.com"
},
"service_provider": {
"description": {
"key": "service_provider.description",
"default_value": "Service Host Provider"
},
"guid": "1431963455",
"support": {
"email": "support@ehrtutor.com"
},
"service_provider_name": {
"key": "service_provider.name",
"default_value": "EHR Tutor"
},
"timestamp": "2017-05-04T05:37:35-05:00"
}
},
"resource_handler": [
{
"resource_name": {
"key": "lesson.resource.name",
"default_value": "EHR Tutor App Launcher"
},
"description": {
"key": "lesson.resource.description",
"default_value": "Launch the EHR Tutor Application"
},
"message": [
{
"message_type": "basic-lti-launch-request",
"path": "lti",
"parameter": [
{
"variable": "User.id",
"name": "user_id"
},
{
"variable": "Person.name.given",
"name": "lis_person_name_given"
},
{
"variable": "Person.name.family",
"name": "lis_person_name_family"
},
{
"variable": "Person.name.full",
"name": "lis_person_name_full"
},
{
"variable": "Person.email.primary",
"name": "lis_person_contact_email_primary"
},
{
"variable": "Membership.role",
"name": "roles"
},
{
"variable": "Context.id",
"name": "context_id"
},
{
"variable": "Context.title",
"name": "context_title"
},
{
"variable": "ResourceLink.title",
"name": "resource_link_title"
},
{
"variable": "CourseSection.sourcedId",
"name": "lis_course_section_sourcedid"
}
]
}
],
"resource_type": {
"code": "lesson"
}
}
]
},
"lti_version": "LTI-2p0",
"security_contract": {
"shared_secret": "lgvupYnu5kaCFMWzLZkWhoKPbRaF89oyPGbTzaTwiYFpe3_c4xdQ2B-CW4-pAQeedzXxKf8h0J-T2O5tjxzFFA=="
},
"@id": "https://canvas.instructure.com/api/lti/courses/1146163/tool_proxy/339b6700-e4cb-47c5-a54f-3ee0064921a9"
}
In the end there were lots of issues that you need to be aware of when trying to register a toolproxy in LTI 2.0
application/vnd.ims.lti.v2.toolproxy+json
oauth_body_hash
in your Authorization
headerIf you are using Python and need the oauth_body_hash using requests-oauthlib you can add force_include_body=True
to the OAuth1 call to get it to happen.
sign = OAuth1(self.launch_params['reg_key'], self.launch_params['reg_password'],
signature_type=SIGNATURE_TYPE_AUTH_HEADER, force_include_body=True)