openfun / richie

:pencil: An opensource CMS to build education portals
https://richie.education
MIT License
262 stars 85 forks source link

Richie enrollment to OpenEDX fails with CSRF No Referrer error #1547

Open sion42x opened 2 years ago

sion42x commented 2 years ago

Bug Report

Expected behavior/code In /richie/src/frontend/js/utils/api/lms/openedx-hawthorn.ts line 123 a triggered enrollment will be called in OpenEDX and successfully enroll the user.

Actual Behavior Richie reports "Your enrollment request failed." The error shows as a 403 on [SET - Enrollment]. Using the network analysis tool, the error returned is:

{"detail":"CSRF Failed: Referer checking failed - no Referer."}

Adding 'Referer': url to the request headers in openedx-hawthorn.ts file does not resolve the issue.

Steps to Reproduce

  1. Create a course in Richie
  2. Sync course runs from OpenEDX
  3. From the course details page, attempt to enroll (you must not already be enrolled)

Environment

Possible Solution

Additional context/Screenshots In this environment:

When stock tutor-richie settings produced the error, the following were also attempted:

sampaccoud commented 2 years ago

This is definitely related to tutor-richie. We'll need your help @regisb

sion42x commented 2 years ago

This is definitely related to tutor-richie. We'll need your help @regisb

I noticed the requirement that Richie and OpenEDX both be sibling subdomains under the same parent domain. What if OpenEDX is the parent domain and Richie is a subdomain? That should also work, correct?

sampaccoud commented 2 years ago

I would say no.

sion42x commented 2 years ago

I would say no.

Thanks, I will organize a test where I switch the domain of OpenEDX to see if it resolves this issue.

EDIT: I tested using Richie as the parent domain and OpenEDX as the subdomain, and also tested using both as sibling subdomains. Both tests returned the same CSRF error.

sion42x commented 2 years ago

I definitely tried everything I could to make it work. Until @regisb is able to chime in, I changed src/frontend/js/components/CourseRunEnrollment/index.tsx on the case step === Step.IDLE: switch section (line 208) to:

      return (
        <React.Fragment>
          <div>
            <a href={courseRun.resource_link} className="course-run-enrollment__cta">
              <FormattedMessage {...messages.enroll} />
            </a>
          </div>
        </React.Fragment>
      );

That way I can keep the logic if they are enrolled already that it displays, but they can go to the course itself to enroll if need be. This will likely be better with eCommerce anyways I'd imagine (until Joanie is ready!). @sampaccoud that might be a worthwhile feature, a simple flag/setting to allow LMS link but disable Enrollment from Richie. If it's enabled, the case block will be empty, if not, the text I provided would work.

regisb commented 2 years ago

As far as I understand, this is related to two issues:

  1. The CSRF token dropped by the LMS is not readable in Richie because it is stored in a cookie associated to the "{{ LMS_HOST }}" domain name. This can be resolved by prefixing the CSRF_COOKIE_DOMAIN by a dot in the LMS -- assuming the Richie host is indeed a subdomain or a sibling domain of the LMS. However...
  2. ... in Richie, the CSRF cookie value is loaded from a cookie named "'edx_csrf_token", and this name is hardcoded: https://github.com/openfun/richie/blob/c34c24e218f4ce32317f2f6cba642020feedad80/src/frontend/js/settings.ts#L6 We are supposed to tell the LMS to write this CSRF cookie for cross-domain requests: https://richie.education/docs/lms-backends#prerequisites But this configuration does not work because the enrollment view is not decorated with @ensure_csrf_cookie_cross_domain in edx-platform (at least not for POST).

I did manage to resolve the issue by manually loading the cookie value from the "csrftoken" cookie name.

Thus, I recommend that:

a. We modify the CSRF_COOKIE_DOMAIN to be .{{ LMS_HOST }} in the tutor-richie plugin. b. We make it possible to customize the CSRF cookie name in Richie. (and we would set it to "csrftoken" in the tutor-richie plugin).

neekesh commented 2 years ago

any updates??

rhoit commented 2 years ago

@regisb can you elaborate the steps to resolve manually by loading the cookie value from csrftoken.

neekesh commented 2 years ago

As far as I understand, this is related to two issues:

  1. The CSRF token dropped by the LMS is not readable in Richie because it is stored in a cookie associated to the "{{ LMS_HOST }}" domain name. This can be resolved by prefixing the CSRF_COOKIE_DOMAIN by a dot in the LMS -- assuming the Richie host is indeed a subdomain or a sibling domain of the LMS. However...
  2. ... in Richie, the CSRF cookie value is loaded from a cookie named "'edx_csrf_token", and this name is hardcoded: https://github.com/openfun/richie/blob/c34c24e218f4ce32317f2f6cba642020feedad80/src/frontend/js/settings.ts#L6

    We are supposed to tell the LMS to write this CSRF cookie for cross-domain requests: https://richie.education/docs/lms-backends#prerequisites But this configuration does not work because the enrollment view is not decorated with @ensure_csrf_cookie_cross_domain in edx-platform (at least not for POST).

I did manage to resolve the issue by manually loading the cookie value from the "csrftoken" cookie name.

Thus, I recommend that:

a. We modify the CSRF_COOKIE_DOMAIN to be .{{ LMS_HOST }} in the tutor-richie plugin. b. We make it possible to customize the CSRF cookie name in Richie. (and we would set it to "csrftoken" in the tutor-richie plugin).

As far as I understand, this is related to two issues:

  1. The CSRF token dropped by the LMS is not readable in Richie because it is stored in a cookie associated to the "{{ LMS_HOST }}" domain name. This can be resolved by prefixing the CSRF_COOKIE_DOMAIN by a dot in the LMS -- assuming the Richie host is indeed a subdomain or a sibling domain of the LMS. However...
  2. ... in Richie, the CSRF cookie value is loaded from a cookie named "'edx_csrf_token", and this name is hardcoded: https://github.com/openfun/richie/blob/c34c24e218f4ce32317f2f6cba642020feedad80/src/frontend/js/settings.ts#L6

    We are supposed to tell the LMS to write this CSRF cookie for cross-domain requests: https://richie.education/docs/lms-backends#prerequisites But this configuration does not work because the enrollment view is not decorated with @ensure_csrf_cookie_cross_domain in edx-platform (at least not for POST).

I did manage to resolve the issue by manually loading the cookie value from the "csrftoken" cookie name.

Thus, I recommend that:

a. We modify the CSRF_COOKIE_DOMAIN to be .{{ LMS_HOST }} in the tutor-richie plugin. b. We make it possible to customize the CSRF cookie name in Richie. (and we would set it to "csrftoken" in the tutor-richie plugin).

we solved this problem by adding referrer policy in both richie and lms in settings. We changed the referrer policy to "no-referrer-when-downgrade" SECURE_REFERRER_POLICY = "no-referrer-when-downgrade"

sion42x commented 2 years ago

we solved this problem by adding referrer policy in both richie and lms in settings. We changed the referrer policy to "no-referrer-when-downgrade" SECURE_REFERRER_POLICY = "no-referrer-when-downgrade"

@neekesh would you mind giving me a little more detail on where I can find that setting for both OpenEDX and Richie? I'd like to try it but making these changes in tutor is not so straightforward.

sion42x commented 2 years ago

@regisb would you be able to provide the right steps to add the change referenced in https://github.com/openfun/richie/issues/1547#issuecomment-1034537800?

we solved this problem by adding referrer policy in both richie and lms in settings. We changed the referrer policy to "no-referrer-when-downgrade" SECURE_REFERRER_POLICY = "no-referrer-when-downgrade"

neekesh commented 2 years ago

we solved this problem by adding referrer policy in both richie and lms in settings. We changed the referrer policy to "no-referrer-when-downgrade" SECURE_REFERRER_POLICY = "no-referrer-when-downgrade"

@neekesh would you mind giving me a little more detail on where I can find that setting for both OpenEDX and Richie? I'd like to try it but making these changes in tutor is not so straightforward.

You have to change it in the tutor file in case of openedx and for richie you have to change inside the docker for openedx (if your server is ubuntu) settings will be in /root/.local/share/tutor/env/apps/openedx/settings/lms/production.py you have to change the csrf cookie domain here CSRF_COOKIE_DOMAIN=".your_domain.com" SECURE_REFERRER_POLICY="no-referrer-when-downgrade" for richie ; to go inside the docker docker exec -it tutor_local_richie_1 bash to change in the settings path /app/richie/sandbox/settings.py you just have to add SECURE_REFERRER_POLICY="no-referrer-when-downgrade"

for further information you can read the following documention of django cors settings. link in the description https://docs.djangoproject.com/en/4.0/ref/settings/ for django referrer policy https://chipcullen.com/django-3-referrer-policy-change/

sion42x commented 2 years ago

You have to change it in the tutor file in case of openedx and for richie you have to change inside the docker for openedx (if your server is ubuntu) settings will be in /root/.local/share/tutor/env/apps/openedx/settings/lms/production.py you have to change the csrf cookie domain here CSRF_COOKIE_DOMAIN=".your_domain.com" SECURE_REFERRER_POLICY="no-referrer-when-downgrade" for richie ; to go inside the docker docker exec -it tutor_local_richie_1 bash to change in the settings path /app/richie/sandbox/settings.py you just have to add SECURE_REFERRER_POLICY="no-referrer-when-downgrade"

Thank you, but I believe that these files will be overwritten when quickstart is run again. Hopefully there is a persistent way to do it with a tutor patch or something like that. I'll give it a try for now!

neekesh commented 2 years ago

You have to change it in the tutor file in case of openedx and for richie you have to change inside the docker for openedx (if your server is ubuntu) settings will be in /root/.local/share/tutor/env/apps/openedx/settings/lms/production.py you have to change the csrf cookie domain here CSRF_COOKIE_DOMAIN=".your_domain.com" SECURE_REFERRER_POLICY="no-referrer-when-downgrade" for richie ; to go inside the docker docker exec -it tutor_local_richie_1 bash to change in the settings path /app/richie/sandbox/settings.py you just have to add SECURE_REFERRER_POLICY="no-referrer-when-downgrade"

Thank you, but I believe that these files will be overwritten when quickstart is run again. Hopefully there is a persistent way to do it with a tutor patch or something like that. I'll give it a try for now!

if you commit the docker after making changes it should work