Tutor Nightly login error: "RSA key must include all parameters if any are present besides d"

Is anyone else getting a 500 back from the login API when trying to auth into recent versions of Tutor Nightly?
image

System info:

  • Xubuntu 22.04
  • Docker 24.0.6
  • tutor, version 16.1.4-nightly
  • edx-platform bind-mounted with a recent version of master

Things I’ve tried:

  • pulling both tutor and edx-platform
  • tutor config save
  • tutor images build openedx openedx-dev
  • using both tutor dev and tutor local
  • running launch in each of those ^
  • re-launching with fresh tutor root with the default config.yml
  • cleared browser cache

LMS logs:

tutor_nightly_dev-lms-1  | 2023-10-19 21:35:19,219 ERROR 61 [django.request] [user None] [ip None] log.py:224 - Internal Server Error: /api/user/v2/account/login_session/
tutor_nightly_dev-lms-1  | Traceback (most recent call last):
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
tutor_nightly_dev-lms-1  |     response = get_response(request)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
tutor_nightly_dev-lms-1  |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
tutor_nightly_dev-lms-1  |   File "/opt/pyenv/versions/3.8.15/lib/python3.8/contextlib.py", line 75, in inner
tutor_nightly_dev-lms-1  |     return func(*args, **kwds)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
tutor_nightly_dev-lms-1  |     return view_func(*args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
tutor_nightly_dev-lms-1  |     return self.dispatch(request, *args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
tutor_nightly_dev-lms-1  |     return bound_method(*args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/views/decorators/debug.py", line 89, in sensitive_post_parameters_wrapper
tutor_nightly_dev-lms-1  |     return view(request, *args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 723, in dispatch
tutor_nightly_dev-lms-1  |     return super().dispatch(request, *args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
tutor_nightly_dev-lms-1  |     response = self.handle_exception(exc)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
tutor_nightly_dev-lms-1  |     self.raise_uncaught_exception(exc)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
tutor_nightly_dev-lms-1  |     raise exc
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
tutor_nightly_dev-lms-1  |     response = handler(request, *args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
tutor_nightly_dev-lms-1  |     return bound_method(*args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
tutor_nightly_dev-lms-1  |     response = view_func(request, *args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 719, in post
tutor_nightly_dev-lms-1  |     return login_user(request, api_version)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
tutor_nightly_dev-lms-1  |     response = view_func(request, *args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django/views/decorators/http.py", line 40, in inner
tutor_nightly_dev-lms-1  |     return func(request, *args, **kwargs)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django_ratelimit/decorators.py", line 27, in _wrapped
tutor_nightly_dev-lms-1  |     return fn(request, *args, **kw)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/django_ratelimit/decorators.py", line 27, in _wrapped
tutor_nightly_dev-lms-1  |     return fn(request, *args, **kw)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 642, in login_user
tutor_nightly_dev-lms-1  |     response = set_logged_in_cookies(request, response, possibly_authenticated_user)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 153, in set_logged_in_cookies
tutor_nightly_dev-lms-1  |     _create_and_set_jwt_cookies(response, request, cookie_settings, user=user)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 299, in _create_and_set_jwt_cookies
tutor_nightly_dev-lms-1  |     jwt = _create_jwt(request, user, expires_in)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 319, in _create_jwt
tutor_nightly_dev-lms-1  |     return create_jwt_from_token(access_token, DOTAdapter(), use_asymmetric_key=True)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 110, in create_jwt_from_token
tutor_nightly_dev-lms-1  |     jwt_token_dict = create_jwt_token_dict(token_dict, oauth_adapter, use_asymmetric_key)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 83, in create_jwt_token_dict
tutor_nightly_dev-lms-1  |     jwt_access_token = _create_jwt(
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 196, in _create_jwt
tutor_nightly_dev-lms-1  |     return _encode_and_sign(payload, use_asymmetric_key, secret)
tutor_nightly_dev-lms-1  |   File "/openedx/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 286, in _encode_and_sign
tutor_nightly_dev-lms-1  |     jwk = PyJWK(key, algorithm)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/jwt/api_jwk.py", line 60, in __init__
tutor_nightly_dev-lms-1  |     self.key = self.Algorithm.from_jwk(self._jwk_data)
tutor_nightly_dev-lms-1  |   File "/openedx/venv/lib/python3.8/site-packages/jwt/algorithms.py", line 433, in from_jwk
tutor_nightly_dev-lms-1  |     raise InvalidKeyError(
tutor_nightly_dev-lms-1  | jwt.exceptions.InvalidKeyError: RSA key must include all parameters if any are present besides d
tutor_nightly_dev-lms-1  | [19/Oct/2023 21:35:19] "POST /api/user/v2/account/login_session/ HTTP/1.1" 500 460304

I ran into this recently on a deployment that is tracking the master branch. The source of the error is this commit. That changes the library being used to generate/read JWTs, requiring an updated structure for the keys used in the settings.

The fix for us was to use the openedx/core/djangoapps/oauth_dispatch/management/commands/generate_jwt_signing_key.py management command to generate new keys. If you run that from the running instance it will default to incorporating the existing keys into the output. That should allow for a smooth transition for running instances to move between the old and new key structures without forcing a logout of existing sessions.

1 Like

Thanks Toby! Now I see Tim’s notes here.

@Tim_McCormack , I see you put that note under Quince page, but the PR merged 2 days ago which is after the 10/9 Quince cut. Would it make sense to move those notes to the Redwood page?

Followup: I’ve updated the release notes to clarify some things, and moved them from Quince to Redwood (which is where the change actually ended up landing). Kyle made a PR in Tutor to generate configs in a way that will work with PyJWT, the library we’re now using for JWT signing.

@blarghmatey It should be possible to “upgrade” the existing key in place using the scripts/jwk-precompute-params.py script mentioned in the Redwood release notes.

1 Like

@kmccormick I seem to be having some trouble with the nightly fix for this. After pulling down the fixed version of tutor I’m unable to regenerate my environment using tutor config save due to this error. If I remove the dq/dp/qi lines it runs fine.

$ tutor config save
Configuration saved to /Users/zhancock/Library/Application Support/tutor-nightly/config.yml
Error rendering template apps/openedx/settings/cms/development.py
Error: Missing configuration value: 'Crypto.PublicKey.RSA.RsaKey object' has no attribute 'dq'

@zhancock_edx Huh. Have you tried reinstalling tutor’s own python reqs? pip install -e . -r requirements/dev.txt

ahh yeah that was it. Thank you!

1 Like

Hi, I ran into the same problem when trying to upgrade tutor from 15.0.0 to 17.0.0. The command pip install --upgrade "tutor[full]" went well but then whatever I try to do (tutor local launch, tutor local save) I always get this error message:

Error rendering template apps/openedx/settings/cms/development.py
Error: Missing configuration value: 'Crypto.PublicKey.RSA.RsaKey object' has no attribute 'dq'

Can you please help me to fix this?

@svkucheryavski This issue was also reported here: Error: Missing configuration value: 'Crypto.PublicKey.RSA.RsaKey object' has no attribute 'dq' · Issue #962 · overhangio/tutor · GitHub

tl;dr:

pip install pycryptodome==3.19.0

Thank you!