Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions docs/decisions/0003-jwt-usage.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
0003: JWT usage
###############

Status
******

**Draft**

Context
*******

Json Web Tokens (JWT) are a way for authenticating users in web
applications. The server generates a signed token that encodes the
user's identity and any relevant claims, which is then sent to the
client. The client includes this token in subsequent requests, allowing
the server to verify the user's identity without needing to maintain
session state.

Because the tokens are cryptographically signed, they can be efficiently
verified by the server without requiring database queries, which brings
performance and scalability benefits.

In addition to storing user identity, JWT tokens can also include
additional claims, such as user roles or permissions, which can be used
for authorization (AuthZ) purposes.

JWT is the `recommended way to authenticate with the Open edX REST API
<https://docs.openedx.org/projects/edx-platform/en/latest/how-tos/use_the_api.html>`_,
however, it's not widely used for AuthZ across the platform. One
exception is the `edx-rbac <https://github.com/openedx/edx-rbac>`_
library, which was a previous attempt to implement role-based access
control in the platform.

The edx-rbac library adds role information to the `"roles" claim in the
JWT token
<https://github.com/openedx/edx-rbac/blob/master/docs/how_to_guide.rst>`_
however, this is not widely used across the platform, being mostly used
on enterprise modules.

Given the new AuthZ project efforts, which seek to unify and simplify
AuthZ on Open edX, there is an opportunity to re-evaluate the use of JWT
tokens for Authorization purposes.

Possible Methods
================
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not actually important but the bullets in this section are nested too far and in the rendered view they view as quotes (https://openedx-authz--34.org.readthedocs.build/en/34/decisions/0003-jwt-usage.html#possible-methods). Probably not actually worth fixing, but worth paying attention to for more user-facing documentation.


#. Including AuthZ data in the JWT, attaching the user-related policies
in the "policy" claim. Example implementation: `casbin-jwt-express
<https://github.com/tiagostutz/casbin-jwt-express>`_

- Pros:

- No additional DB queries needed for AuthZ, which brings
potential performance and scale benefits.

- Cons:

- JWT tokens can become large and unwieldy, potentially
impacting request performance, increasing bandwidth
usage, and hitting size limits.

- Changes in user roles or permissions will not be
reflected in the token until it is refreshed, leading to
potential security issues.

#. Not including AuthZ data in the JWT

- Pros:

- Smaller token size, leading to improved request
performance and reduced bandwidth usage.
- Changes in user roles or permissions can be reflected
immediately without requiring a token refresh.

- Cons:

- Additional DB queries may be needed for AuthZ, which
could impact performance and scalability (could be
mitigated with caching strategies).

Discussion
==========

The new AuthZ approach being discussed in this repository will allow for
a unified and more flexible way to manage authorization, this will allow
for more complex authorization scenarios to be handled more easily, for
example, specifying fine-grained access controls for libraries.

On a big instance, this will mean that the policies attached to a user
will potentially grow larger and more complex, which would greatly
increase the data that a JWT token would need to carry if we were to
include AuthZ data in the token itself.

On the other hand, the performance and resources impact of doing AuthZ
checks in the backend on every request can be mitigated with caching
strategies.

Decision
********

We will not include AuthZ data in the JWT tokens. JWT tokens will
continue to be used as an authentication mechanism, but AuthZ will be
handled separately in the backend.
Comment on lines +101 to +103
Copy link
Copy Markdown
Member

@mariajgrimaldi mariajgrimaldi Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally agree with this approach!

Currently, I can only see those roles being passed currently directly in the JWT payload: https://github.com/openedx/edx-platform/blob/acbf50a7dd8492a9fb80f35ffbc561b668fe619d/openedx/core/djangoapps/oauth_dispatch/jwt.py#L275-L276 for administrator and superuser roles. We haven't decided whether to use those django-native definitions still or move them into the authZ. What makes more sense to me, simplicity-wise, is to keep using those and implement other feature-specifics into the authz system but we'd need a way of integrating them seamlessly. In any case, that would mean continuing to send the same roles in the same format. What do you think?

Copy link
Copy Markdown
Contributor Author

@rodmgwgu rodmgwgu Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the existing roles will continue being passed to the JWT as-is, and for now will still be picked by the current auth code.

The new AuthZ implementation won't directly care about these, unless we find any special case that would need it, in that case we would do an "OR" on the specific action, for example:

Let's say that we identify that superuser should always have access to "read" any Library, on the GET library endpoint handler, we would validate like (pseudo code):

has_access = is_superuser() or new_authz_has_permission("library-read", user, library)

where is_superuser() is the existing code that validates this, and new_authz_has_permission is our new implementation.

Would this make sense?

Copy link
Copy Markdown
Member

@mariajgrimaldi mariajgrimaldi Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that makes total sense. I think this is the simplest approach given the current state.

Another case came to my attention last week while I was discussing with my team about how the new authz framework should interact with other systems: Aspects. Currently, this is how the system manages access to its resources:
https://github.com/openedx/tutor-contrib-aspects/blob/e0db9f51250fd842dcdcce8abb57befc17cff0ae/tutoraspects/templates/aspects/apps/superset/pythonpath/openedx_sso_security_manager.py#L179-L208 - roles are sent in the JWT. It looks like we only use admin and superuser -I think- so it should work the same.

If this is not the case, then we'll need to decide on our next steps for handling this specific case of authorization management, but it's not an immediate blocker. Should we document this somewhere?

FYI @bmtcril

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if the existing JWT implementation doesn't change that is fine for Aspects' use of the superuser role to grant access to all reports.

However in the long term, the way Aspects uses the courses endpoint to find which roles the user has on individual courses will have to change. Ideally we would have an endpoint that would allow us to get all courses that a specific user has a specific permission on instead of having to query all courses that the user is associated with, then do further API calls to determine if they have the necessary permission to view reports for each course.

I'm not sure those details are relevant to this ADR, but we may want to capture the generic use case for "external service needs to check LMS/Studio permissions over API" somewhere.


Consequences
************

- JWT tokens will continue to be used as they are currently, primarily
for authentication purposes.
- AuthZ will be handled in the backend, caching strategies should be
considered to mitigate performance impacts.

Rejected Alternatives
*********************

- Including AuthZ data in the JWT tokens by using the "policy" claim,
including the full ACL definition for the user.

References
**********

- `edx-rbac how to guide <https://github.com/openedx/edx-rbac/blob/master/docs/how_to_guide.rst>`_
- `Example implementation of jwt embedded policies with casbin <https://github.com/tiagostutz/casbin-jwt-express>`_
- :ref:`OEP-66: Authorization Best Practices <openedx-proposals:OEP-66 User Authorization>`
- :ref:`Open edX Auth Overview Table <Open edX Auth Overview Table>`