instructure / canvas-lms

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

Cannot launch LTI 1.3. assignments in IFrame: Canvas fails to authenticate because cross-site cookie is blocked #1900

Open jvdm opened 3 years ago

jvdm commented 3 years ago

Summary:

In Google Chrome > 91, when using Canvas as an LTI 1.3 platform to launch External Tool assignments connected to an LTI 1.3. tool inside an IFrame, the launch fails with error: login_required and error_description: Must have an active user session.

It is noticeable that log_session_id, the cookie used by Canvas for session management, is blocked during the LTI 1.3. OIDC authentication flow on the request to the authorization endpoint /api/lti/authorize_redirect. This behavior happens since Chrome > 91 started enforcing the new SameSite policies. That policy is blocking cross-site cookies without SameSite=None and Secure set, and redirects within IFrames are considered cross-site.

Steps to reproduce:

  1. Have Google Chrome > 91 (or any other browser enforcing SameSite=None cookies for cross-site calls).
  2. Setup a development stack of Canvas 2021-05-26.01
  3. Configure LTI 1.3.
  4. Configure an LTI tool (I am using a tool that is based on pylti1p3).
  5. Setup an external tool assignment.
  6. Uncheck the "launch in a separate tab" checkbox.
  7. Launch the assignment.
  8. Observe the LTI launch failing. This can be done through the development console.

Expected behavior:

The LTI launch should succeed.

Actual behavior:

The LTI launch fails, here's a breakdown of the requests.

# From To Description
1. BROWSER CANVAS Request assignment in Canvas, which is an External Tool (LTI).
2. CANVAS BROWSER Reply with an empty IFrame and set the log_session_id cookie without SameSite=None. This means cross-site calls to CANVAS will not set the cookie.
3. BROWSER TOOL Request login/ from within the iframe. This is cross-site. It starts the OIDC flow. The frontend code will populate the iframe with the content of this request.
4. TOOL BROWSER Reply with a redirect to CANVAS passing the LTI 1.3. auth parameters. This request does not have log_session_id set. It is blocked in Chrome (but Firefox allows it).
5. BROWSER CANVAS Redirect to authenticate at Canvas is sent, it fails because we are not passing the log_session_id token.
6. CANVAS BROWSER Reply with a redirect to TOOL, with an error.
7. BROWSER TOOL This should be the final tool launch, but it is the error from the authentication.
8. TOOL BROWSER Since it was an error, TOOL reply the "Invalid Tool Launch" message.

Excerpt of the error redirect from /api/lti/authorize:

utf8: ✓
authenticity_token: <a base 64 encodied string that was omited by me>
error: login_required
error_description: Must have an active user session
state: state-891953f5-97dd-4891-837f-5d01958aeb29

Additional notes:

Please, notice there are other resources in Forums and discussions around LTI 1.3. and cross-site cookies handling after the SameSite=None enforcing was added to browsers such as Chrome and Safari, but they are related to updating Tools that uses cross-site cookies. This report is about to perform the LTI 1.3 authentication within an IFrame.

Examples:

  1. https://community.canvaslms.com/t5/Canvas-Developers-Group/Safari-13-1-and-LTI-Integration/ba-p/273051
  2. https://community.canvaslms.com/t5/Canvas-Developers-Group/SameSite-Cookies-and-Canvas/ba-p/257967
leeyongl5263 commented 3 years ago

I am experiencing the same thing. Could you find any remediation or fixes for this?

Thank you :)

NaurizAitbai commented 2 years ago

I have this problem too. When I manually change _legacy_normandy_session to Secure,SameSite=None in my Chrome, it starts working.

jbergfi commented 2 years ago

This is a fairly old issue but I'm seeing the same thing. Just went through making sure SameSite=None and Secure flags are set for the tool I'm working on but this seems to be something that needs to be fixed Canvas side.

I'm quite surprised, if this is an issue, why isn't it an issue for all LTI 1.3 deep linking tools?

NaurizAitbai commented 2 years ago

This is a fairly old issue but I'm seeing the same thing. Just went through making sure SameSite=None and Secure flags are set for the tool I'm working on but this seems to be something that needs to be fixed Canvas side.

Hello. Sorry for my bad english.

That's indeed a problem on Canvas side. Bad documentation. Canvas has session_store.yml file (https://github.com/instructure/canvas-lms/blob/master/config/session_store.yml.example) that has secure field (commented) and you need to uncomment, then it will set SameSite and Secure flags. But you won't find it in their documentation for production installing (https://github.com/instructure/canvas-lms/wiki/Production-Start), so people install self-hosted Canvas, forgetting uncomment secure field, and half of LTI 1.3 won't work on their instance.

Also Canvas has very bad realization for LTI 1.3. You can identify LMS instance by two fields issuer and client_id. So these two fields together needs to be globally unique. BrightSpace, Moodle and other LMS systems don't have problem with uniqueness, because they're generating client_id randomly and issuer defaults to instance domain.

But Canvas LMS, set issuer to https://canvas.instructure.com in https://github.com/instructure/canvas-lms/blob/master/config/security.yml.example and not saying people how to change it in production documentation. Not only this, but their client_id not generating randomly, starting from 10000000000001 for every self-hosted instance.

I'm quite surprised, if this is an issue, why isn't it an issue for all LTI 1.3 deep linking tools?

I think they're using Instructure's Canvas instance. This instance doesn't have this problem, because secure field was set.

jbergfi commented 2 years ago

Adding the session_store.yml config does add the Secure flag which also is needed but as far as I can tell it does not help with adding the SameSite flag. I managed to get it working on my self-hosted instance of Canvas by doing the changes in my pull request above. I had to change in a few different places to make it apply SameSite both to the session cookie and to the CSRF Token cookie.

I'm running a self-hosted instance to test my tool and it has indeed been a bit of a hassle to setup. Not sure if things behaves differently in any Instructure hosted solutions.