openedx / openedx/openedx-platform
[LTI Provider] iframe-embedded problem submissions fail with CSRF 403 under Django cookie defaults
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 8.2k
- Forks
- 4.4k
- Avg merge
- 6d 18h
- Merged PRs (30d)
- 42
Description
Summary
When Open edX is used as an LTI 1.1 tool provider and content is launched inside an iframe on the consumer LMS (verified with Canvas), the initial launch renders correctly, but any subsequent interactive POST (e.g. submitting a problem) is rejected by Django's CSRF middleware with:
Forbidden (CSRF cookie not set.): /courses/.../xblock/.../handler/xmodule_handler/problem_check
The same content works if launched in a new tab / top-level window.
Steps to reproduce
- Configure an Open edX instance as an LTI 1.1 provider (default settings, no changes to
SESSION_COOKIE_SAMESITE/CSRF_COOKIE_SAMESITE). - In Canvas, add an external tool pointing to a problem component or subsection in the Open edX course.
- Launch the tool inside Canvas (iframe, default behavior).
- Attempt to submit an answer.
Expected
The problem submission succeeds and (if the component is graded) the score is passed back to Canvas.
Actual
POST to the xblock handler returns HTTP 403. LMS logs:
WARNING django.security.csrf ... Forbidden (CSRF cookie not set.): /event
WARNING django.security.csrf ... Forbidden (CSRF cookie not set.): /courses/<course>/xblock/<block>/handler/xmodule_handler/problem_check
[pid: 30] POST /event => 403
[pid: 31] POST /courses/<course>/xblock/<block>/handler/xmodule_handler/problem_check => 403
Root cause
lti_launch(lms/djangoapps/lti_provider/views.py) is@csrf_exempt, so the initial launch POST from Canvas works.- Subsequent XBlock handler POSTs are standard CSRF-protected views.
- Django defaults are
SESSION_COOKIE_SAMESITE = 'Lax'andCSRF_COOKIE_SAMESITE = 'Lax'(lms/envs/common.pydoes not override them; it only definesDCS_SESSION_COOKIE_SAMESITE). - Browsers do not send
Laxcookies on cross-site POSTs from the Canvas iframe, so the CSRF cookie is absent and Django rejects the request.
Environment
- Open edX master sandbox
- LTI version: 1.1
- Consumer LMS: Canvas
- Browser: Firefox 149 (macOS 10.15)
Related code
lms/djangoapps/lti_provider/views.py(lti_launch view,@csrf_exempt+@add_p3p_header)lms/envs/common.py(DCS_SESSION_COOKIE_SAMESITEexists, butSESSION_COOKIE_SAMESITE/CSRF_COOKIE_SAMESITEdo not)lms/djangoapps/lti_provider/users.py(require_user_accountenforcement)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with lms/djangoapps/lti_provider/views.py and lms/envs/common.py, then trace the lti_launch flow and Django cookie settings involved in the iframe request. Reproduce the Canvas iframe launch and problem submission, checking the CSRF cookie and handler response. Done means the interactive POST succeeds in the iframe while top-level launches continue to work.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- django, python
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100