Celebrate Excellence in Education: Nominate Outstanding Educators by April 15!
Found this content helpful? Log in or sign up to leave a like!
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.
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.
To participate in the Instructure Community, you need to sign up or log in:
Sign In