-
Notifications
You must be signed in to change notification settings - Fork 6
docs: add ADR on JWT usage #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| ================ | ||
|
|
||
| #. 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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>` | ||
There was a problem hiding this comment.
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.