Skip to content
Snippets Groups Projects
Commit ab4ec931 authored by Robert Raposa's avatar Robert Raposa Committed by Nimisha Asthagiri
Browse files

Update ADR around HTTP_USE_JWT_COOKIE

Update the ADR to clarify another rollout strategy with
HTTP_USE_JWT_COOKIE using the new
JwtRedirectToLoginIfUnauthenticatedMiddleware and new permission class
LoginRedirectIfUnauthenticated.

ARCH-1051
parent 4461d978
No related branches found
No related tags found
No related merge requests found
......@@ -61,8 +61,8 @@ Login -> Cookie -> API
* Contains only the public key signature portion of the JWT.
* Enable HTTPOnly_ so the signature is unavailable to JS code. See `JWT Cookie Security`_ below.
#. **Automatically recombine and extract the JWT from Cookies on API calls.**
* We will create a new middleware that will reconstitute the divided JWT from its two cookies and store the
#. **Automatically recombine and extract the JWT from Cookies on API calls.**
* A new middleware JwtAuthCookieMiddleware will reconstitute the divided JWT from its two cookies and store the
recombined JWT in a temporary cookie specified by JWT_AUTH_COOKIE_.
* The `Django Rest Framework JWT`_ library we use makes use of the JWT_AUTH_COOKIE_ configuration setting.
When set, the JSONWebTokenAuthentication_ class `automatically extracts the JWT from the cookie`_. Since all
......@@ -81,12 +81,19 @@ Login -> Cookie -> API
* To prevent this issue, we will introduce a new HTTP header called "HTTP_USE_JWT_COOKIE" that will be selectively
set only by microfrontends that want to use JWT cookie based authentication. The new middleware will check for
this header before trying to reconstitute and use the JWT token.
* Additionally, select login-required APIs can be updated to redirect the caller to the Login page when the JWT
expires. This can be accomplished by enabling `JwtRedirectToLoginIfUnauthenticatedMiddleware`_ in the Django
service and updating the API to require the `LoginRedirectIfUnauthenticated`_ permission class. The middleware
automatically sets "HTTP_USE_JWT_COOKIE" for incoming requests to APIs that require the
`LoginRedirectIfUnauthenticated`_ permission.
.. _`Lightrail's design`: https://medium.com/lightrail/getting-token-authentication-right-in-a-stateless-single-page-application-57d0c6474e3
.. _Django Rest Framework JWT: https://getblimp.github.io/django-rest-framework-jwt/
.. _JWT_AUTH_COOKIE: https://github.com/GetBlimp/django-rest-framework-jwt/blob/master/docs/index.md#jwt_auth_cookie
.. _JSONWebTokenAuthentication: https://github.com/GetBlimp/django-rest-framework-jwt/blob/0a0bd402ec21fd6b9a5f715d114411836fbb2923/rest_framework_jwt/authentication.py#L71
.. _automatically extracts the JWT from the cookie: https://github.com/GetBlimp/django-rest-framework-jwt/blob/0a0bd402ec21fd6b9a5f715d114411836fbb2923/rest_framework_jwt/authentication.py#L86-L87
.. _JwtRedirectToLoginIfUnauthenticatedMiddleware: https://github.com/edx/edx-drf-extensions/blob/0351010f1836e4cebd6bdc757d477b2f56265b17/edx_rest_framework_extensions/auth/jwt/middleware.py#L76
.. _LoginRedirectIfUnauthenticated: https://github.com/edx/edx-drf-extensions/blob/0351010f1836e4cebd6bdc757d477b2f56265b17/edx_rest_framework_extensions/permissions.py#L147
JWT Cookie Lifetime
......@@ -106,7 +113,7 @@ JWT Cookie Lifetime
#. **Revocation with short-lived JWTs** Given the tradeoff between long-lived JWTs versus immediacy of revocation, we
need to configure an appropriate expiration value for JWT cookies. In a future world with an API gateway, we *may*
have longer lived JWTs with a *stateful* check against a centralized `JWT blacklist`_ and each JWT uniquely
identified by a `JWT ID (jti)`_. In the meantime, we will err on the side of security and have short-lived JWTs.
identified by a `JWT ID (jti)`_. In the meantime, we will err on the side of security and have short-lived JWTs.
#. **Refresh JWT Cookies.** When a JWT expires, we do not want to ask the user to login again while their browser
session remains alive. A microfrontend will detect JWT expiration upon receiving a 401 response from an API
......@@ -129,7 +136,7 @@ JWT Cookie Content
supported by a browser. `Modern browsers have treated this requirement as a maximum`_ - and hence do not support
more than 4096 bytes. Our current JWT size is about 970 bytes (varying with size of user identifiers, like user's
name, etc). (Side note: Signing a JWT with a 2048 byte asymmetric key increases the JWT's size by 325 bytes.)
To minimize the JWT's size from the start, we should eliminate any unnecessary data that is `currently embedded
in the JWT`_. For example:
......@@ -149,12 +156,12 @@ JWT Cookie Security
#. **Enable CSRF Protection.** Storing JWTs in HTTP cookies is potentially vulnerable to CSRF attacks.
See `JWT Cookie Storage Security`_. To protect against this:
* Enable the HttpOnly_ flag on the **"JWT Signature Cookie"**, so Javascript code cannot misuse the JWT.
* Enable the Secure_ flag on the cookie, so it will not be sent (and thus leaked) through an unencrypted channel.
* Enable `Django's CSRF middleware`_ for every response.
* Ensure all GET requests are side-effect free.
* Note: The `same-origin policy`_ protects against CSRF attacks on GET requests since the rogue website cannot
access the response from the GET request.
* However, even though the rogue website cannot access the response, the GET request is still processed on the
......@@ -181,7 +188,7 @@ JWT Cookie Security
Consequences
------------
#. Since session cookies have a limited size of `at least 4096 bytes`_, we will need to monitor its size increase
#. Since HTTP cookies have a limited size of `at least 4096 bytes`_, we will need to monitor its size increase
over time and implement a warning before it exceeds the size. Having this hard limit requires us to be judicious
of what data is included in the JWT. A bloated JWT is not necessarily a benefit to overall web performance.
......@@ -199,6 +206,7 @@ Consequences
#. Continue to use and expand the current `JS-accessible user-info cookie`_, which contains user-data.
#. Have the server populate the initial DOM with this data, but this would only work for server-generated HTML.
.. _at least 4096 bytes: http://browsercookielimits.squawky.net/
.. _JWT sessionStorage and localStorage Security: https://stormpath. com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage#so-whats-the-difference
.. _JS-accessible user-info cookie: https://github.com/edx/edx-platform/blob/70d1ca474012b89e4c7184d25499eb87b3135409/common/djangoapps/student/cookies.py#L151
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment