Community

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
mismailzai
New Member

What key must we validate lti_message_hint (the encoded JWT) against?

Hi all, we're working on an LTI 1.3 integration and have successfully implement the OAuth2 validation steps.

We're at the point now that we have a link displayed in our course, and when the link is clicked, Canvas displays our LTI application in an IFRAME and runs the user through an OAuth2 workflow. Once we've validate the user in this manner, we can also successfully validate our application and receive an application token. Thus, we know who the user is and we can successfully query Canvas on their behalf using our application token.

Our application is receiving a payload from Canvas that includes an iss, login_hint, client_id, target_link_uri, and lti_message_hint (the signed JWT). It's this final step that we're having trouble with. We understand that we must validate the JWT to ensure the message has not been tampered with and is indeed coming from Canvas. Can someone clarify what key this JWT needs to be validated against? We've tried the Canvas public keys with no luck.

Sample code in any programming language would be greatly appreciated.

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Edited on June 15 at 12:39 PM to add more details:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

For example, here is a sample $_POST response (client_id and target_link_uri scrubbed):

array(6) {
["iss"]=>
string(30) "https://canvas.instructure.com"
["login_hint"]=>
string(40) "e20984648b488fb11b323741425126dca07aa300"
["client_id"]=>
string(17) "00000000000000000"
["target_link_uri"]=>
string(86) "https://OurLtiApplication/someEndpoint"
["lti_message_hint"]=>
string(423) "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJpZmllciI6IjhhOGFjNDQ0NDJjNjkxMDFjYjRiMzVkZjQyNTA5NzhhNjUyYzhmNTY0YWU3MDhjOTcxMGE1OTQ2ODAzMTY3MzUyMjA3YTczNjQ2YjRlOTA1NTYyNzM1MDMxNDM1ZGY1MzU3MDQ5Y2Q1YzMyNWYwMDA5N2Y0YjQ4YTA5MWQwMTA5IiwiY2FudmFzX2RvbWFpbiI6ImNhbnZhcy5zdGdlb3JnZXMuYmMuY2EiLCJjb250ZXh0X3R5cGUiOiJDb3Vyc2UiLCJjb250ZXh0X2lkIjo0OTcwMDAwMDAwMDAwMDg2MSwiZXhwIjoxNTkyMjQ5NzY4fQ.KmI75uFTAZBWe9F1mZ6nXnpabWVKDUm4HS6HjP-jExw"
["canvas_region"]=>
string(9) "us-east-1"
}

When we decode the lti_message_hint above, we get the following header:

{
"typ": "JWT",
"alg": "HS256"
}

and the following payload

{
"verifier": "8a8ac44442c69101cb4b35df4250978a652c8f564ae708c9710a5946803167352207a73646b4e905562735031435df5357049cd5c325f00097f4b48a091d0109",
"canvas_domain": "canvas.stgeorges.bc.ca",
"context_type": "Course",
"context_id": 49700000000000860,
"exp": 1592249768
}

So now we just need to validate the signature on the lti_message_hint -- which according to the OAuth2 spec (Validate JSON Web Tokens ) should be done using our client_secret. When we attempt to do so, the signature does not match.

Labels (1)
6 Replies
svickers2
Community Member

If you're asking about verifying the signature of the JWT, then try retrieving the publc key from an endpoint such as https://canvas.test.instructure.com/api/lti/security/jwks or https://canvas.instructure.com/api/lti/security/jwks.  In PHP one of the JWT libraries I use does this automatically once it knows the endpoint to use.

Hi Stephen, thanks for responding. Are you able to share sample code?

I'm using the Firebase PHP JWT library (GitHub - firebase/php-jwt: PHP package for JWT ) and having trouble validating using the key from https://canvas.test.instructure.com/api/lti/security/jwks. Specifically, my library is giving me a 'Signature verification failed' error.

With Firebase I just use a line like this:

  JWT::decode($jwtString, $publicKey, array('RS256'));

where $publcKey can be either the public key in PEM format or the return value from the parseKeySet method.  This method can be used to parse the response from the JWKS endpoint;.e.g.

  $publicKey = JWK::parseKeySet($jwks);

Hi Stephen, thanks for that. I've updated my original post with some additional details. Specifically, the JSON I'm receiving has been signed using the HS256 algorithm, which I believe means I need to verify using the client_id instead of the JWKS.

When I attempt to do so via the code below, I receive a Signature verification failed message:

JWT::decode($jwtString, $client_id, array('HS256'))

The lti_message_hint should be treated as an opaque value which is merely included in your response.  It does not require any validation.  It can be any string (JWT or otherwise) which the platform chooses.

Thank you Stephen, much appreciated.