-
Notifications
You must be signed in to change notification settings - Fork 673
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
Add JWT httpOnly cookie storage. #157
base: master
Are you sure you want to change the base?
Conversation
Very nice! I've tested this locally and it seems to work for me. It does however not deal with the possible CSRF attacks as discussed in #71. In the documentation we should:
@davesque If we get this out of the way, could you look into merging this or is there still anything to discuss in the original issue? |
What's the status of completing this PR? We would like to use this :) |
@NiyazNz - thanks for your hard work on this. Are you aware of any issues with the current implementation? I’d like to integrate this ASAP. |
@NiyazNz Thanks for your work. My project also customized in a similar way. I looking forward to integrating with this work as soon as possible. |
Same as the above, would be awesome to implement this into the library. |
Any chance this PR gets merged soon ? Is there any reason I'm missing why it is not ? |
Also waiting for this one. |
Also waiting |
I think, it would be very useful if you update TokenVerifyView with verification token from cookie. Now we have to pass token in request body |
Also waiting for this one. I do not fully understand what is delaying PR. Is it something that lefts yet? |
It's because I wrote this library for free and have a day job. |
Dave I know that. My question is technically speaking what lefts to make the PR. Thanks for your job. |
Fair enough. I guess the honest answer is that I don't know because I haven't really had time to review it. On the other hand, it's a largish PR so the risk of something bad creeping in is higher. Because of that, I don't want to just merge it in if the tests pass without giving it a good look. My suggestion to people is that they should pip install their own fork with a git url (see here) if they urgently need this functionality. Otherwise, they'll have to wait until I have time to review it and merge it in. |
Aah oks! I understand. Thanks! I'll do the fork. |
You can use the original fork from NiyazNz, It's working perfectly https://github.com/sieira/pycont/blob/master/api/build/requirements.base.txt#L4 Happy new year everyone, by the way |
Could the TokenVerifyView be modified to verify the tokens in cookies as well? |
I am very... confused as to the purpose of this? Why would you use JWT authentication for web when you have session cookies already? So assuming this is for the web application / the website, here's my opinion: If anything, you're going to be implementing a lot more JS to grab the cookied tokens which means a new point of attack (as previously mentioned in the XSS and CSRF token misplacements or nonexistence). Seems more like a headache to me; providing this would be a disservice. DRF should really only be used for mobile application and occasionally be used with AJAX on an insecure endpoint. Sure, you can tack on some IP rate limit, but, in reality, you should be using a user rate limit and sending 403 forbidden back to unauthenticated users. But with a user rate limit, you're sending refresh tokens via http which can easily be eavesdropped on... so again: What's the point of this? Feels more like a security vulnerability to users than a practical feature. Also a reminder: I could also be interpreting this issue incorrectly :) lemme know if I did. |
@Andrew-Chen-Wang because of this response |
@Andrew-Chen-Wang I empathize with your concerns. However, for better or for worse, single page apps that use JWTs as proof of auth are extremely common these days. For that use-case, it's generally preferred that auth credentials are not kept in local storage just in case an attacker succeeds in performing XSS. The better option is to keep them in an http-only cookie. Then you can still get the benefits of stateless auth (avoid using session keys for DB-backed sessions) but use the more traditional delivery method for tokens (in cookies). |
any update to merge this PR? |
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.
I'm not entirely sure how to use this added feature. An example project would be nice to have, some React + Django would be nice to see for other users to be able to copy. I recall glossing over an article using Axios, but I don't use any of these rising frontend technologies. Once an example project comes up, then I'll run some more checks. For now, I've just left some basic comments.
I have an example project for Swift iOS and Kotlin Android at https://github.com/Andrew-Chen-Wang/mobile-auth-example. You can make a PR there since a Django project is already setup.
permission_classes = () | ||
|
||
def post(self, request): | ||
response = Response({}) |
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.
Is it necessary to have an empty dict inside the response? Shouldn't it just be Response()
?
from datetime import datetime | ||
|
||
from django.middleware import csrf | ||
from django.utils.translation import ugettext_lazy as _ |
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.
ugettext_lazy
deprecated in favor of gettext_lazy
.
# A string like "example.com", or None for standard domain cookie. | ||
'AUTH_COOKIE_DOMAIN': None, | ||
# Whether the auth cookies should be secure (https:// only). | ||
'AUTH_COOKIE_SECURE': False, |
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.
Since you're working with CSRF, you should instead use this Django setting instead of False
: CSRF_COOKIE_SECURE. Same for the rest of the settings. Set the defaults to the CSRF counterparts.
'AUTH_COOKIE_PATH': '/', | ||
# Whether to set the flag restricting cookie leaks on cross-site requests. | ||
# This can be 'Lax', 'Strict', or None to disable the flag. | ||
'AUTH_COOKIE_SAMESITE': 'Lax', |
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.
Similarly to the AUTH_COOKIE_SECURE: CSRF_COOKIE_SAMESITE
@@ -132,6 +132,64 @@ refresh token to obtain another access token: | |||
... | |||
{"access":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3BrIjoxLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiY29sZF9zdHVmZiI6IuKYgyIsImV4cCI6MTIzNTY3LCJqdGkiOiJjNzE4ZTVkNjgzZWQ0NTQyYTU0NWJkM2VmMGI0ZGQ0ZSJ9.ekxRxgb9OKmHkfy-zs1Ro_xs1eMLXiR17dIDBVxeT-w"} | |||
|
|||
JWT httpOnly cookie storage |
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.
You'll need to merge with master and move this documentation to where the rest of the documentation is now
""" | ||
check = CSRFCheck() | ||
# populates request.META['CSRF_COOKIE'], which is used in process_view() | ||
check.process_request(request) |
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.
Hmm, try setting: CSRF_COOKIE_HTTPONLY = True
return response | ||
|
||
def get_refresh_token_expiration(self): | ||
return datetime.now() + api_settings.REFRESH_TOKEN_LIFETIME |
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.
Is datetime.now()
the expected behavior? The server could be anywhere, the client can be anywhere, so perhaps using django.utils.timezone.now
would be better since it will always use UTC server side. The method set_cookie
expects a datetime object in UTC: https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpResponse.set_cookie
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.
Idk if L124 would be a problem then.
def set_auth_cookies(self, response, data): | ||
response.set_cookie( | ||
api_settings.AUTH_COOKIE, data['token'], | ||
expires=datetime.now() + api_settings.REFRESH_TOKEN_LIFETIME, |
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.
Again, should probably use timezone.now()
instead of datetime.now()
@@ -34,6 +34,18 @@ | |||
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', | |||
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), | |||
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), | |||
|
|||
# Cookie name. Enables cookies if value is set. | |||
'AUTH_COOKIE': None, |
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.
I feel like this should have a better name, more verbose like AUTH_COOKIE_NAME
like the CSRF_COOKIE_NAME
.
path=reverse(self.token_refresh_view_name), | ||
secure=api_settings.AUTH_COOKIE_SECURE or None, | ||
httponly=True, | ||
samesite='Strict', |
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.
SameSite='strict'
Slightly concerning if a dev creates a subdomain (e.g. Instagram's misc. webpages).
Hello! I think there is also some more work to be done in the doc, we'd have to move what in the README to the official doc, you may want to add a few tests etc... So there is still quite a bit of work to be done and if people don't want it, I'll just stop here and I'll just maintain the fork. This is what I have at the moment: If you want to test it just add this line |
Hi all, I've found an article regarding React + Django by using session authentication: https://www.valentinog.com/blog/drf/ Please let me know if this is a suitable option and easier for everyone. I am still not convinced about this usage of SimpleJWT in a browser, but who knows until someone hacks into it properly. Again, let me know if that blog about using a single template will work for people using React, Angular, and Vue.js! @loicgasser @derek-adair Also, any provided example would be great to see. |
Serving my SPA via Django is a No Go for me. For the scope of my current project this is the right solution. I might need to serve some mobile applications with the same back-end in the future. Using JWT from the start might spare me a few headaches later on. |
I have similar situation with current project, so work on this is greatly appreciated. Using Django + React as decoupled API + SPA (option 2 from cited article (https://www.valentinog.com/blog/drf/), mobile apps will be required in the future. So far i have been using @NiyazNz fork with no issues. Switching to @loicgasser breaks using refresh token in cookie, i believe the problem is in views.py line 69 , where Also, the link to install the fork does not work, use this instead: git+git://github.com/AtuzSolution/django-rest-framework-simplejwt@jwt_cookie#egg=rest_framework_simplejwt |
Thanks @TychoVrahe yes you're correct the last commit was untested, I just fixed it (which means there is a test missing there.). |
cool, seems to be working fine now |
Friendly reminder for those asking for a merge/push. This project is looking for maintainers |
I think this can be merged so long as the communication of the token isn't too frequent. An example repository SHOULD be setup as a de facto setup with this package in ALL frontend frameworks. Hearing about a DRF SimpleJWT hack in thousands of websites is not what anyone wants to hear. I worry about XSS and DOS. If XSS happens, your site's kinda screwed altogether and no one would really care too much about getting someone's token (which could still happen if someone like a president or official gets hacked). I originally worried about XSS mostly because indirectly it affects your website's performance (gravely due to authentication.py. Edit: also forgot to mention, for the most part, people can't do firewalls and network security groups correctly... so basically DNS and SSH stuff happens...). TL;DR There needs to be a default setup for SimpleJWT. That means using middle wear like Axios or something like that. @davesque can create repositories for each framework as template repositories (I guess for now, they'll be under my name, but Dave can transfer them to SimpleJWT organization). Once there are at least two frameworks setup, then this could be merged. |
I didn't know I could create repositories as a Triage member, but I can. Please take a look at the organization's repositories and contribute. That way, red+blue teamers can try it out asap and we can see a merger soon. Thank you! Edit: The repositories are setup. Please go and fork them, and please contribute! You may update the requirements.txt file. When updating the requirements.txt file and pointing the requirement for simplejwt to a git repository, you MUST specify a commit SHA. Otherwise, we cannot faithfully track down which commit works and which doesn't. It must be consistent with updates. React repository: https://github.com/SimpleJWT/drf-SimpleJWT-React VueJS repository: https://github.com/SimpleJWT/drf-SimpleJWT-Vue iOS and Android is already listed in the example repositories. You can find that here: https://github.com/Andrew-Chen-Wang/mobile-auth-example If I am missing a framework, please generate a template from here: https://github.com/SimpleJWT/drf-SimpleJWT-server-template Good luck and thanks for contributing! Remember, if you can set up a template repository, then this PR can be merged quicker. At least two JS frameworks will be needed with unit tests to be considered for merge. Also, please use Travis with a .travis.yml file. That yellow circle, red X, and green check makes taking a look at everything much easier to the eye and to the brain. It also allows passer-bys to think that the repository is actually good-to-go. |
thanks @Andrew-Chen-Wang for setting up the repositories. I use Angular but I have some knowledge of React. I'll see what I can do to help. Something to consider for the next django versions. If you want to explicitly set the |
@loicgasser I just made an Angular repository if you'd like to contribute! Regarding the SameSite cookie that loicgasser mentioned, it should be noted more in the added docs. |
is there any update for when this will be merged and released officially? |
We'll need time to review it carefully @bsdis . David and I are just busy, so please be patient. It's been awhile since both of us have even looked into this PR just because it's so large and it's changing a lot. |
I think it's time to close this issue. Please refer to the tutorial at https://github.com/Andrew-Chen-Wang/SPA-with-sessions to use Django server-backed sessions with the httpOnly flag for authorization purposes. The integration will help you build a React app, deploy it on GitHub pages in a separate repository, and contain your frontend and backend in one monolithic repository (if you want). A React + Django demo can be found at https://acwpython.pythonanywhere.com/authenticated/ (if you go to index, you're not logged in. That authenticated url is where I automatically authenticate you with a pre-made user). Further discussion of that tutorial is at #360 I can see that there may still be some use case for this, such as those who just don't want a server backing up their session state, but I also think the cons outweighs the pros this time around, e.g. security in this one PR alone, overhead complexity for the developer, the nature of stateless authorization (especially if it's for cookie/browser use, personal belief and the belief of the DSF: not good), etc.. Please let me know your thoughts in #360 though. I just wanted to state my opinion and tutorial here first. |
I'd also like to see this merged. |
Bumping ... please implement this ! Many thanks |
Can we get some feedback from the maintainers what is blocking this PR? We are eagerly awaiting this functionality and would like to contribute if possible to get this merged. So some guidance from the team on how to proceed would be great. |
this PR needs to be updated to reflect main/master and address comments. Like I've said recently, I'm willing to merge an updated PR, but I will not have time as a single maintainer to constantly look for security issues and not have time to rapidly publish patches as school and work ramps up again. |
Thanks for this👋🏼 |
refs #71