instructure / canvas-lms

The open LMS by Instructure, Inc.
https://github.com/instructure/canvas-lms/wiki
GNU Affero General Public License v3.0
5.57k stars 2.47k forks source link

LTI 1.3's lti1p1 claim's oauth_consumer_key_sign calculated using a nonce not in the payload #2287

Open JonathanGawrych opened 10 months ago

JonathanGawrych commented 10 months ago

Summary:

oauth_consumer_key_sign is calculated using the oauth_consumer_key, deployment_id, iss, client_id, exp, and nonce, joined together using the ampersand &, and signed using hmac-sha256. This allows LTI 1.3 tools to automatically and securely upgrade LTI 1.1 installs. We finished our implementation of calculating the key sign, and found ours did not match the one provided in the lti1p1 claim.

Steps to reproduce:

  1. Go to Admin -> pick your account -> settings -> Apps -> View App Configurations -> +App -> Fill out the LTI 1.1 information, making note of the Launch Url
  2. Create a course.
  3. Create an assignment. Use submission type "External Tool" finding and selecting the tool above.
  4. Launch the assignment, showing the LTI 1.1 tool does have the right oauth consumer key/secret
  5. Go back to Admin -> pick your account -> settings -> developer tools -> +Developer Key -> +LTI key -> Fill out the LTI 1.3 information. Make sure to put the LTI 1.1 Launch Url into the Target Link URI field.
  6. Relaunch the assignment, now in LTI 1.3. This will provide the oauth_consumer_key_sign

Expected behavior:

The oauth_consumer_key_sign should be signed using the nonce given in the payload

Actual behavior:

The oauth_consumer_key_sign is signed using some other nonce

Additional notes:

You can see that the nonce passed into the generate_oauth_consumer_key_sign method is created here in JwtMessage's add_security_claims. It's a uuid but that's not what comes in the payload. In the payload, the nonce is nonce-<64hexchars>. The nonce is overwritten here in the authentication_controller's cached_launch_with_nonce. Because of this, it is impossible for the LTI 1.3 tool to use oauth_consumer_key_sign

mzohaiba commented 4 months ago

I am also facing the same problem. @JonathanGawrych Did you find out any solution or response from canvas ?

JonathanGawrych commented 4 months ago

Unfortunately not. LMS oauth_consumer_key_sign support proved to be quite abysmal across LMSs

As for fixes and workarounds, we briefly considered just allowing LTI 1.3 launches to auto-migrate without oauth_consumer_key_sign. However, this would allow anyone to take over any LTI 1.1 org with a crafted malicious LTI 1.3 launch. We instead built a manual tool on our side to approve LTI 1.1 -> LTI 1.3 migration securely and link them with the correct LTI 1.1. Unfortunately that means when you migrate to LTI 1.3, you get a "contact our support", and our support has to verify your identity, verify you are indeed an admin in the org you are trying to migrate, and verify what LTI 1.1 to link it to, and the users are blocked until it is done. Our documentation for LTI 1.3 migration asks you to contact support first so we can minimize downtime.

Even then we still do actually support oauth_consumer_key_sign, but that code path has never been triggered by a customer, sadly. However, we had to put in code that says "ignore the key sign if canvas and version is less than x.x.x" and plan to set that canvas version when this was fixed... but it never was.

Another issue that would affect this is that Canvas in some edge cases picks the incorrect oauth_consumer_key. Suppose you have two LTI 1.1 installs, one at the org level, and one at the subaccount level, then have two LTI 1.3 migrations, also at the org and subaccount level.

So even if they fixed oauth_consumer_key_sign, you may accidentally tie the wrong LTI 1.3 deployment (the account level one) to the wrong LTI 1.1 install (the org level one). That seperate bug would also need to be fixed before we enabled key sign for canvas.

For all these reasons we built our manual tool and excluded canvas from using oauth_consumer_key_sign. I hope this helps in your journey to support LTI 1.3.

mzohaiba commented 4 months ago

@JonathanGawrych Your input has been incredibly valuable and has helped me tremendously in navigating through some challenges. Your expertise and suggestions have truly made a difference, and I'm deeply grateful for your support.

JonathanGawrych commented 4 months ago

@mzohaiba In a surprising twist of events, I just got a notification after 6 month of radio silence that Blackboard has release oauth_consumer_key_sign in version 3900.83. We were very surprised, as they hinted it wasn't possible. We tested it, and it seems work out of the box, for both new and existing activities. It was very exciting!

However, they didn't use to send a lti1p1 claim, but now they do, this time with a user_id. Unfortunately, that user_id doesn't match the LTI 1.1 user_id, but seems to be some internal blackboard user id. This caused our system to create duplicate users when they launch. We had to release an update to ignore blackboard's lti1p1 claim's user_id. Aside from that, it seems like we will actually have an LMS that'll use oauth_consumer_key_sign!