Search code examples
moodlecanvas-lmslti

LTI 2 tool Proxy registration is failing with 400 or 500 error


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:

  • http://lti.tools/test/tc.php (Registers Succesfully)
  • Moodle 3.2 running on AWS (400 Bad Request)
  • Moodle 3.3 runnong on Localhost (Registers Succesfully)
  • Canvas (400 Bad Request) {"error":"Invalid Capabilities"}

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": "[email protected]"
                        },
                        "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": "[email protected]"
            },
            "service_provider": {
                "description": {
                    "key": "service_provider.description",
                    "default_value": "Service Host Provider"
                },
                "guid": "1431963455",
                "support": {
                    "email": "[email protected]"
                },
                "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"
}

Solution

  • 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

    1. Make sure that your content-type is application/vnd.ims.lti.v2.toolproxy+json
    2. Make sure that you have an oauth_body_hash in your Authorization header
    3. Check the spelling and case of all your JSON keys
    4. Make sure that any parameters that you ask for in your resource handlers are in the capability_offered list of the tool_consumer_profile
    5. Use reg_key and not the guid in the tool_consumer_profile as the tool_proxy_guid/oauth_consumer_key. The guid is the same as the reg_key on Moodle, but the guid is a constant value in Canvas.

    If 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)