...
Mobile needs a way to call some ecommerce payment APIs using JWT tokens.
Mobile is trying to switch switching the mobile authentication flow from opaque (Bearer) access tokens to JWTs to simplify the edx-platform authentication story.
Note: This may require additional work, and may not need to be a blocker for the ecommerce work.
See OAuth2 and Mobile for some past discussion on this topic.
Ownership:
Arch-BOM squad owns Open edX authentication.
Security Working Group has security expertise.
Mobile squad owns mobile experience, including authentication.
Current Proposal
...
Implement switch from opaque access tokens (Bearer) to JWT access tokens by making the following changes:
Request the initial access token be returned using the JWT format rather than the opaque (Bearer) format.
Update the exchange of 1st party access token for new login session to accept JWT in addition to Bearer access token.
Update the exchange of 3rd party access token for 1st party access token to accept JWT in addition to Bearer access token.
High-level Feedback
...
For all changes, we need to be careful about potential security risks.
...
When the mobile squad wishes to proceed on the Bearer to JWT, there are a number of additional changes that would be required. See later section with more specific feedback.
...
Documenting Decision
We must write an ADR (Architectural Decision Record) to capture the current context, any decisions being made, and their consequences.
...
One possible location for the new ADR is: https://github.com/openedx/edx-platform/tree/master/openedx/core/djangoapps/oauth_dispatch/docs/decisions
We should update or replace the parent doc, OAuth2 and Mobile, which is referenced by the Authentication OEP.
Bearer to JWT Proposal Feedback
...
Currently, the JWT access token has a 10 hour lifetime, but it should have a maximum lifetime of 1 hour (matching the current lifetime of JWT cookie). Tim McCormack notes that JWTs should probably have a max of 15 minutes in all cases, rather than 1 hour, but that is a separate potential discussion.
Private ticket for this work: https://2u-internal.atlassian.net/browse/ARCHBOM-2099
User Account Status
For 1st-party token exchange for session login using JWTs, it is especially important to ensure that the user account has not been disabled, since the JWT is non-revocable. We can use Django’s has_usable_password for this.
This new check should apply to either token type. Also note that if an account is disabled, any active session is removed as explained here.
Password Grant Check
For the currently proposed 1st-party token exchange for session login using JWTs, we would need an equivalent check for _is_grant_password to not expand permissiveness of the endpoint.
This would require changes. Some options may include:
...
We will add the grant type into the JWT payload,
...
To keep down size, maybe this is added as-needed and the decoder can fill in the default.
...
Maybe we could use the JWT token as the Bearer Token in the database so the grant type can continue to be accessible via the database lookup?
See BearerToken docs as reference.
Pros:
This would continue to make the JWT revocable for this use case.
The DOT access token wouldn’t be a string that is never actually used.
Cons:
I’m guessing all of our Bearer tokens would be in the JWT format, but use the “Bearer” header, which may get confusing.
I’m not clear on all of the implications of a change like this.
so that it can be checked when used.
Asymmetrically Signed JWT
...
Asymmetric signing indicates that we're probably distributing the public (verification) key to the other IDAs and only exposing the private (signing) key to the LMS, which is very important if JWTs can be "escalated" to sessions.
It is unclear how this would be done. This information may be in the signature? Tim McCormack may have ideas.
Alternative Bearer to JWT Proposals
Alternative 1
For creating a session, in place of the token exchange endpoint, maybe it would be possible to create the session during the original access token request using an additional query parameter? Here we have access to the Password Grant information.
Note: I’m unclear about the 3rd-party to 1st-party token exchange, and if that would also require some of the above discussed validation, or if it would just work under this alternative proposed flow.
Alternative 2
Mobile can make an access token request for a JWT token with the refresh token and use it where it is required, and continue to use the existing Bearer token for initiating a session and the 3rd-party to 1st-party token exchange.
Notes:
...
This would be a short-term fix that unblocks the ecommerce work, but doesn’t fully solve removing the Bearer tokens.
...
Decision:
Add method to edx-drf-extensions like get_decoded_jwt_from_auth, but that will decode only asymmetric JWTs.
Probably requires a call to decode or decode_complete, providing a list of just the asymmetric algorithms.
It would be nice if we could provide a good error message for symmetric JWTs.
Configure mobile oauth client as a restricted client and test that it gets asymmetric JWTs.
Rejected Alternative:
Complete https://2u-internal.atlassian.net/browse/ARCHBOM-1161 (marked as Done, but is unfinished) so the LMS only supports symmetric JWTs. Note: although this is the right way to go, it will probably take too long, so we will continue to defer.