JWT and Juniper?

There is one thing for which I would really like “documentation” when we will be testing Juniper.

How do we create the different JWT ?
What values do we put where in lms.env.json, cms.env.json or anywhere else ?

A written almost real-life example would definitely benefit me or other operators in the Open edX community.

Or maybe it is simply me not understanding how to create the tokens and where each value created fits in the different JSON files. I hope I am not alone in this situation…

I should probably add some background.

I recently tried installing a version of “master” for Open edX. I needed to test something without our fork.

Normally, at least with ironwood and previous open-releases, after installation I can login as audit@example.com, staff@example.com and a few other users. Now, I can’t…

Looking at /edx/var/log/lms/edx.log I see the following error messages whenever I try to log in with these users:

Nov 18 17:15:09 ip-10-0-0-208 [service_variant=lms][django.request][env:sandbox] ERROR [ip-10-0-0-208  19883] [user None] [exception.py:135] - Internal Server Error: /user_api/v1/account/login_session/
Traceback (most recent call last):
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response
    response = self._get_response(request)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 185, in inner
    return func(*args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 67, in _wrapper
    return bound_func(*args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/views/decorators/debug.py", line 76, in sensitive_post_parameters_wrapper
    return view(request, *args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 63, in bound_func
    return func.__get__(self, type(self))(*args2, **kwargs2)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 433, in dispatch
    return super(LoginSessionView, self).dispatch(request, *args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/rest_framework/views.py", line 494, in dispatch
    response = self.handle_exception(exc)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/rest_framework/views.py", line 454, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/rest_framework/views.py", line 491, in dispatch
    response = handler(request, *args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 67, in _wrapper
    return bound_func(*args, **kwargs)
  File "/edx/app/edxapp/edx-platform/openedx/core/lib/api/view_utils.py", line 371, in _wrapped
    return func(request)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 63, in bound_func
    return func.__get__(self, type(self))(*args2, **kwargs2)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 67, in _wrapper
    return bound_func(*args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 63, in bound_func
    return func.__get__(self, type(self))(*args2, **kwargs2)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 429, in post
    return shim_student_view(login_user, check_logged_in=True)(request)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 501, in _inner
    response = view_func(request)
  File "/edx/app/edxapp/venvs/edxapp/local/lib/python2.7/site-packages/django/utils/decorators.py", line 149, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/views/login.py", line 362, in login_user
    return set_logged_in_cookies(request, response, possibly_authenticated_user)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 151, in set_logged_in_cookies
    _create_and_set_jwt_cookies(response, request, cookie_settings, user=user)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 260, in _create_and_set_jwt_cookies
    jwt = _create_jwt(request, user, expires_in)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/user_authn/cookies.py", line 280, in _create_jwt
    return create_jwt_from_token(access_token, DOTAdapter(), use_asymmetric_key=True)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 73, in create_jwt_from_token
    filters=oauth_adapter.get_authorization_filters(client),
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 134, in _create_jwt
    return _encode_and_sign(payload, use_asymmetric_key, secret)
  File "/edx/app/edxapp/edx-platform/openedx/core/djangoapps/oauth_dispatch/jwt.py", line 214, in _encode_and_sign
    serialized_keypair = json.loads(settings.JWT_AUTH['JWT_PRIVATE_SIGNING_JWK'])
  File "/usr/lib/python2.7/json/__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python2.7/json/decoder.py", line 382, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

It seems there is a problem with the default values in /edx/app/edxapp/lms.env.json and /edx/app/edxapp/cms.env.json and I would really like to put correct values so that I could log in.

Any ideas?

@sambapete is this still an issue, or was it related to the json/yaml configuration change?

No longer an issue as I found a way to generate the keys afterwards. We can close this issue.

Can you help me with how to help other people with this? Should there be any code changes? Doc changes?

You can find a potential solution for Problem B (the generation of JWT keys) under Juniper Alpha basic installation

This is the answer from the other post:

I will investigate whether there is a better answer than this.

1 Like

Yes, that is an appropriate solution. That management command generates the keys that you need to sign and verify JWTs. For security reasons, we need each installation to generate its own set of keys so we do not ship with default values.

Is there a place in the Juniper installation process where running the management command and copying over the keys can be automated? Or can automation of this be tackled by the build-test-release working group?

There is a generate-passwords.sh step in the Native installation. We can add more to it.

@nimisha Who can help with the details?

Looks like the docs for this question have been added to the Juniper Confluence under Django Configuration and SetUp thank you!

@sambapete Can this post be marked SOLVED and CRI-173 closed?

Should (can?) this be automated in the Native install? Or do we need to add more steps there?

I am in favour of both closing it and automating it in the Native install. I am afraid that if it isn’t automated people will ask again and again and again why they can’t login to their site anymore or where to find the instructions. Not everyone has the same technical background as you @nedbat or @jill or me @sambapete

Just my 2 canadian cents. If found a piggy bank full of them while cleaning up my apartment recently. We do not have a cent currency in Canada for the last few years.