dj-rest-auth
dj-rest-auth copied to clipboard
Impossible to refresh a JWT if JWT_AUTH_HTTPONLY is True
From the docs:
/dj-rest-auth/token/refresh/ (POST) (see also)
- refresh Returns access
Note USE_JWT = True to use token/refresh/ route. Note Takes a refresh type JSON web token and returns an access type JSON web token if the refresh token is valid. HTTP 401 Unauthorized with {"detail": "Token is invalid or expired", "code": "token_not_valid"} in case of a invalid or expired token.
Then if you visit the (see also) link, here is what it says:
When this short-lived access token expires, you can use the longer-lived refresh token to obtain another access token:
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImNvbGRfc3R1ZmYiOiLimIMiLCJleHAiOjIzNDU2NywianRpIjoiZGUxMmY0ZTY3MDY4NDI3ODg5ZjE1YWMyNzcwZGEwNTEifQ.aEoAYkSJjoWH1boshQAaTkf8G3yn0kapko6HFRt7Rh4"}' \
http://localhost:8000/api/token/refresh/
...
{"access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNTY3LCJqdGkiOiJjNzE4ZTVkNjgzZWQ0NTQyYTU0NWJkM2VmMGI0ZGQ0ZSJ9.ekxRxgb9OKmHkfy-zs1Ro_xs1eMLXiR17dIDBVxeT-w"}
So what's the issue?
if JWT_AUTH_HTTPONLY is set to True, refresh returns an empty string when a login request is made. So how can one refresh an expired JWT if the above endpoint expects a refresh token to be present in the payload?
As described in the docs:
refresh_token will not be sent if JWT_AUTH_HTTPONLY set to True, set it to False if you need refresh_token.
HTTPOnly cookies are sent with every request, and the very point is that Javascript cannot access them. Getting them in the response would defeat that.
Arguably it shouldn't return the access token either, as you shouldn't be using it. You need to store the expiration times and use them to determine if the credentials will still be valid, as well as appropriately handle 401 errors.
To refresh with JWT_HTTP_ONLY on, you post an empty body to the refresh endpoint and ensure your javascript is configured to send credentials (cookies) with the request. (In angular you add the parameter withCredentials: true to the http.post)
If you're past the expiration date of the access token or get a 401, post empty to refresh and store the updated access_expiration, and if you're past the refresh expiration date or get a 401 from refresh, then prompt the user for re-authentication.
@rostgoat
/token/refresh View, accept the refresh_token on the body, not in the headers.
So, when you use a httponly flag in the cookies, it's no longer accessible by the Javascript. And for that, you can't add the refresh_token as a payload.
Note: If the httponly flag is enabled in the cookies, the browser automatically adds the cookies on every api request.
That's the main purpose of using a httponly flag
So the question is, if we can't access the cookies. How are we gonna send the refresh_token on the server and retrieve a new access_token ?
The answer is that you have to move the headers cookies to the payload
This is something like a preflight process. The client sent a request on the server with appropriate cookies headers, and when it was received by the server, It should extract the refresh_token from the request headers and added to payload. That's it !
You can archive this by simply creating a middleware.py and adding the middleware to your django settings.py
Here is the middleware code! https://github.com/iMerica/dj-rest-auth/issues/97#issuecomment-739942573
This issue was solved 4 years ago,
In 2024, it should be added to this project. Don't know why it has still not been added,
Thanks to @Wolf-Byte
+1
+1
@rostgoat
/token/refreshView, accept therefresh_tokenon the body, not in the headers.So, when you use a
httponlyflag in the cookies, it's no longer accessible by theJavascript. And for that, you can't add therefresh_tokenas a payload.Note: If the
httponlyflag is enabled in the cookies, the browser automatically adds the cookies on every api request.That's the main purpose of using a
httponlyflagSo the question is, if we can't access the cookies. How are we gonna send the
refresh_tokenon the server and retrieve a newaccess_token?The answer is that you have to move the
headerscookies to thepayloadThis is something like a preflight process. The client sent a request on the server with appropriate cookies headers, and when it was received by the server, It should extract the
refresh_tokenfrom the request headers and added topayload. That's it !You can archive this by simply creating a
middleware.pyand adding the middleware to your djangosettings.pyHere is the middleware code! #97 (comment)
This issue was solved 4 years ago,
In 2024, it should be added to this project. Don't know why it has still not been added,
Thanks to @Wolf-Byte
There is no need to add refresh token to request. The refresh cookie will directly be used from the cookie headers to produce a new access token for the user.
See https://github.com/iMerica/dj-rest-auth/blob/master/dj_rest_auth/jwt_auth.py#L84
I am using this functionality without any issues. I believe this particular issue should be closed.