dmitry-viskov / pylti1.3

LTI 1.3 Advantage Tool
MIT License
120 stars 64 forks source link

Seeking clarification on the django login process, and nonces #73

Open perllaghu opened 2 years ago

perllaghu commented 2 years ago

I have a [I think] sane django app... the service has been running for a few years, and happily uses both LTI 1.1 and SAML2 based authentication.

For reference: Django 3.1.13, running in a kubernetes cluster.

I'm now adding LTI 1.3 authentication, and my launch process was failing.

I believe I've narrowed it down to the login process not setting a nonce ... for the launch process to then pick up on & verify.

This in my login code:

def lti1p3login(request):
    logger.info(f"#### lti 1.3 login called")

    tool_conf = get_tool_conf()
    launch_data_storage = get_launch_data_storage()

    oidc_login = DjangoOIDCLogin(request, tool_conf, launch_data_storage=launch_data_storage)
    target_link_uri = get_launch_url(request)
    foo = oidc_login.enable_check_cookies().redirect(target_link_uri)
    # .redirect should have called ._prepare_redirect_url - which should have set stuff
    logger.info(f"    oidc example nonce (pylti1p3/oidc_login - lines 114-116): {oidc_login._generate_nonce()}")  # lti1p3-nonce
    nonce_key = oidc_login._session_service._get_key('nonce')
    logger.info(f"    oidc nonce (pylti1p3/oidc_login - lines 114-116): {oidc_login._session_service._get_value(nonce_key)}")  # lti1p3-nonce
    session_service = oidc_login._session_service
    logger.info(f"    launch storage : {launch_data_storage}")
    logger.info(f"Returning oidc_login redirect {foo}")
   return foo

This logs:

{"message": "#### lti 1.3 login called", "timestamp": "2022-03-28T10:17:39.186512+00:00", "level": "INFO"}
{"message": "    oidc example nonce (pylti1p3/oidc_login - lines 114-116): 9f5e401c7def42cba7049935bc1971aa4bc4c44aae8011ecaec4c6252675a8d8", "timestamp": "2022-03-28T10:17:39.187694+00:00", "level": "INFO"}
{"message": "    oidc nonce (pylti1p3/oidc_login - lines 114-116): None", "timestamp": "2022-03-28T10:17:39.199934+00:00", "level": "INFO"}
{"message": "    launch storage : <pylti1p3.contrib.django.launch_data_storage.cache.DjangoCacheDataStorage object at 0x7f105f9fd0d0>", "timestamp": "2022-03-28T10:17:39.200087+00:00", "level": "INFO"}
{"message": "Returning oidc_login redirect <HttpResponse status_code=200, \"text/html; charset=utf-8\">", "timestamp": "2022-03-28T10:17:39.200663+00:00", "level": "INFO"}
127.0.0.1 - - [28/Mar/2022:11:17:39 +0100] "POST /lti1p3/login/ HTTP/1.1" 200 3246 .....<snip>.....

Am I wrong in assuming that the redirect() call should have set something.... and that something should be retrievable from oidc_login._session_service._get_value(nonce_key)?


Further info - after some investigation - it may be a cookie issue, if one of my colleagues does some "clever magic" so things appear as localhost, it all works.

perllaghu commented 2 years ago

I needed to switch to using a database-cache for the django app.

I can't tell you if the problem is kubernetes related, or proxy-related... either way: switching the cache fixed my problem

hmoffatt commented 2 years ago

Now that you mention it I had the same problem (though I use Flask). I use gunicorn with multiple workers. Without a proper cache configuration the cache is not shared between workers, so requests fail unless they happen to hit the same worker.

jsogg commented 6 months ago

I was getting a lot of random nonce validation errors until I changed gunicorn from 4 workers to 1 worker. I haven't seen the validation issue since changing.

hmoffatt commented 6 months ago

I was getting a lot of random nonce validation errors until I changed gunicorn from 4 workers to 1 worker. I haven't seen the validation issue since changing.

That is because you don't have a shared cache.