Skip to content
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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

NiyazNz
Copy link

@NiyazNz NiyazNz commented Sep 7, 2019

refs #71

@hanneskuettner
Copy link

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.
I would suggest going the same route as jpadilla/django-rest-framework-jwt#434 and injecting the csrf cookie in TokenViewBase responses. I would suggest making this configurable just as as in the linked PR and adding similar documentation.

In the documentation we should:

  • Give an example how to protect a view against CSRF using @method_decorator(csrf_protect), as DRF exempts all APIViews from CSRF protection
  • Mention that the user can define a custom CSRF_FAILURE_VIEW that provides a nice DRF-like response, as opposed to the standard HTML output django generates

@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?

@jonakirke94
Copy link

What's the status of completing this PR? We would like to use this :)

@SHxKM
Copy link

SHxKM commented Oct 11, 2019

@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.

@sookyeomKim
Copy link

@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.

@knasti
Copy link

knasti commented Oct 16, 2019

Same as the above, would be awesome to implement this into the library.

@sieira
Copy link

sieira commented Dec 21, 2019

Any chance this PR gets merged soon ?

Is there any reason I'm missing why it is not ?

@marekfs
Copy link

marekfs commented Dec 23, 2019

Also waiting for this one.

@agorbunoff
Copy link

Also waiting

@ghost
Copy link

ghost commented Dec 26, 2019

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

@stgoddv
Copy link

stgoddv commented Dec 31, 2019

Also waiting for this one. I do not fully understand what is delaying PR. Is it something that lefts yet?

@davesque
Copy link
Member

davesque commented Jan 2, 2020

It's because I wrote this library for free and have a day job.

@stgoddv
Copy link

stgoddv commented Jan 2, 2020

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.

@davesque
Copy link
Member

davesque commented Jan 2, 2020

My question is technically speaking what lefts to make the PR.

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.

@stgoddv
Copy link

stgoddv commented Jan 2, 2020

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.

Aah oks! I understand. Thanks! I'll do the fork.

@sieira
Copy link

sieira commented Jan 2, 2020

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

README.rst Outdated Show resolved Hide resolved
@OofYeetMcGee
Copy link

Could the TokenVerifyView be modified to verify the tokens in cookies as well?

@Andrew-Chen-Wang
Copy link
Member

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.

@rodrigondec
Copy link

@Andrew-Chen-Wang because of this response
#157 (comment)

@davesque
Copy link
Member

@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).

@UmarFarooq-Ch
Copy link

any update to merge this PR?

Copy link
Member

@Andrew-Chen-Wang Andrew-Chen-Wang left a 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({})
Copy link
Member

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 _
Copy link
Member

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,
Copy link
Member

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',
Copy link
Member

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
Copy link
Member

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)
Copy link
Member

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
Copy link
Member

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

Copy link
Member

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,
Copy link
Member

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,
Copy link
Member

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',
Copy link
Member

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).

@loicgasser
Copy link

loicgasser commented Jun 18, 2020

Hello!
I have implemented a version of this pull request with DRF and Angular running on different servers. If this has a chance to be merged, I could try to provide a minimal example of how it works and how it could be implemented in a SPA.

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:
master...AtuzSolution:jwt_cookie
Which cleaned up and rebased version of
#157
which also includes
#175

If you want to test it just add this line
git+git://github.com/AtuzSolution/django-rest-framework-simplejwt@jwt_3#egg=rest_framework_simplejwt

@Andrew-Chen-Wang
Copy link
Member

Andrew-Chen-Wang commented Jun 18, 2020

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.

@loicgasser
Copy link

loicgasser commented Jun 18, 2020

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.
If someone can hack into this setup, please show me the code.
As I said I am happy maintaining my fork aside, I just wanted to share.

@TychoVrahe
Copy link

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 self.token_refresh_data_key was probably meant to be used.

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

@loicgasser
Copy link

loicgasser commented Jun 19, 2020

Thanks @TychoVrahe yes you're correct the last commit was untested, I just fixed it (which means there is a test missing there.).
That's why I gave a link to a release which I then deleted 😄 to create jwt_3. (updated the original link)
This is what I currently use in dev.
git+git://github.com/AtuzSolution/django-rest-framework-simplejwt@jwt_3#egg=rest_framework_simplejwt

@TychoVrahe
Copy link

cool, seems to be working fine now

@derek-adair
Copy link

Friendly reminder for those asking for a merge/push. This project is looking for maintainers

@Andrew-Chen-Wang
Copy link
Member

Andrew-Chen-Wang commented Jul 1, 2020

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.

@Andrew-Chen-Wang
Copy link
Member

Andrew-Chen-Wang commented Jul 3, 2020

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.

@loicgasser
Copy link

loicgasser commented Jul 9, 2020

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 SameSite cookie to None (for instance to avoid Google Warnings) we will need to use a string. 'None' or 'none'.

django/django@b33bfc3

@Andrew-Chen-Wang
Copy link
Member

@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.

@bsdis
Copy link

bsdis commented Sep 13, 2020

is there any update for when this will be merged and released officially?

@Andrew-Chen-Wang
Copy link
Member

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.

@Andrew-Chen-Wang
Copy link
Member

Andrew-Chen-Wang commented Jan 16, 2021

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.

@jwhitehorn
Copy link

I'd also like to see this merged.

@simevo
Copy link

simevo commented Apr 12, 2021

Bumping ... please implement this ! Many thanks

@MichaelAnckaert
Copy link

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.

@Andrew-Chen-Wang
Copy link
Member

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.

@ahmadOlawale008
Copy link

Thanks for this👋🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.