Hello,
Is there a way to generate or authenticate a token to grant access only for a specified view and be invalid on other views?
This may sound odd but I want to issue a JWT token to share some info with frontend and grant access to a sensitive view, I'm using JWT at another view too but it is less important than this one.
When I started to think about both, I found that if someone swapped one with another it will pass through verification and authentication as it's a valid JWT token issued by me but not for a specific view, so is there a way to do that?
Should I make my custom JWT authentication class, verify class and check for my required payload too? or there is something more simple? or should I use something else, but JWT is the one here because you need to share some info with the client.
Should I change secret and issue the new one with a different secret? should I use PyJWT? I don't care about linking it to a user, I'm even using AnonymousUser for the existing one, what do you think?
Thanks, everyone
This is not the point of SimpleJWT. SimpleJWT is an authentication library, but it's not widely used as an authorization library, which is what you seek. If you want per-view access, you need an authorization library; in that case, you might as well use django OAuth toolkit.
If you don't want to set up OAuth, you can include a token claim (sample in documentation) that specifies a "custom" code for a view. Let's say you had a view called "PingViewSet". You'd access the token claim and see if the token claim that specifies if the user is allowed in the view. The token claim'll be unencrypted since it's in the payload, but it won't matter since the user can't edit the token (it'll fail authentication).
Thanks for the fast response!
I'm actually using django OAuth toolkit at my project but the need to share data doesn't leave any other choices, forgive me if my description made an impression that I'm seeking an authorization level but I think it's more like a new or separate JWT authentication class at my system so each one is on its own.
Your suggestion should work, I was thinking about the same to be done at verification endpoint but at the view level, I prefer if we could come up with something that works on authentication level or permission levels like overriding or writing my custom class for those.
Sure, no problem.
Hm a new authentication class / new authentication.py's? Or something like DRF permission classes / Django built-in permission model? Perhaps you could provide an example; I'm just slightly confused what you mean. Although, it seems like whatever I suggested means that I do understand... This part specifically somewhat confuses me:
the need to share data doesn't leave any other choices
The default authentication class suggested in the doc just uses the PyJWT package to verify the user. I think you're trying to say something like object-level permission, right? Let's say you had a url like: example.com/post/123/ in which 123 is the post's pk. You give a token to the user but then that user gives/swaps with another user. That other user shouldn't have that token.
The following is what I assume you're talking about:
What you've described is a Man in the Middle attack in which the second user stole the first user's token. This is also why SPA (e.g. React and Vue) is not officially supported and is still a WIP for this package.
SimpleJWT is a stateless method for authentication (usage of JWT in general is stateless). The Django built-in method for authentication is session cookies, a stateful method of authentication which prevents this "swapping," which use the httpOnly flag. Currently, SimpleJWT can't prevent users from "giving away" their tokens. The only way they can be "given" away is through code error or manually giving it away.
Please let me know if I'm getting this wrong. I think it's best you give me an example of what you're trying to accomplish.
Again, Thank you for the time and effort you put into this, I really appreciate that!
What you've described is a Man in the Middle attack in which the second user stole the first user's token. This is also why SPA (e.g. React and Vue) is not officially supported and is still a WIP for this package.
This is the case where I'm exactly concerned for, I'm using JWT to give access for anonyms users to submit their feedback, it's an invitation URL link with a JWT token attached to, so whoever has the correct link will submit their feedback, now I want to use JWT to complete my user's signup, but at first glance, I thought that if a malicious person takes a JWT token from an invitation link and tried to access the signup page with it he will pass authentication as it still valid for JWTTokenUserAuthentication to pass although it hasn't the correct payload and should fail later it should be like that with such a sensitive resource.
So as you can see it's on a view authentication not on an object level.
I will start with some code snippets first to end the confusion, I think it explains the idea better:
I was thinking for something like this
class CompleteSignup(viewsets.GenericViewSet):
authentication_classes = [JWTCustomSignupAuthentication]
Where JWTCustomSignupAuthentication inherits from a BaseJWTAuth class or something and I write down my logic fro this, like using another secretKey to verify the token or the correct payload, ... etc.
I started to think that different secretKey will be enough to accept or reject a token immediately.
In this case, you shouldn't be using JWT at all. Verification links work by sending the user a random link which expires in which authentication isn't needed at all (check out django-allauth. It does exactly what you describe and is very extensible and feature rich). For signup, you're sending a verification via EMAIL which shouldn't be able to be MITM attacked if sent to the user. Again try django-allauth.
Are you saying that you're sending a link with credentials for automatic sign in upon entering the link?
I wasn't confused about the code implementation. The confusing part is the concept you're trying to explain.
I completely agree with you about django-allauth, but it is not a verification link.
I don't have a user at that moment, I'm sending the email to start the signup process, the user has entered some values previously like email and first&last name then proceeds to payment, on successful payment a user should receive a link start the signup process, the info entered before should be used again at the client as prefilled or sent with the new data he enters for a signup, so I have to share that info, my choices for JWT because I can do both at once (authentication and share info), I know that this can be done by all auth style but there will be more steps to achieve that, I thought that JWT is the right choice for that - or that's what I thought-.
Hm I just started using Django stripe, and even without djstripe, many payment services, even if you're custom making one, really want you to have a user first. Anyways,...
What you're describing is the same method as verification links. I believe they're hash generated. The object allauth creates contains the email and confirms is at L154 (they use django.core.signing). This is so that you can include a payload of a specified record in a database that points to the purchase (you do still have a record of the payment, right, for tax purposes?). Doesn't seem like too many steps to me.
Also, adding some random info to the JWT is harmful. The payload of a JWT is not encrypted.
What you're describing is the same method as verification links. I believe they're hash generated. The object allauth creates contains the email and confirms is at L154 (they use django.core.signing). This is so that you can include a payload of a specified record in a database that points to the purchase (you do still have a record of the payment, right, for tax purposes?). Doesn't seem like too many steps to me.
This sounds helpful, but the last point is not my use case as there is so much redundant data that I won't save to my payment record so I'm only keeping a transaction_id along with email so I can get full details when needed form payment service and avoid data redundancy, with JWT I will only include these on the fly like first name and last name and forget about it but with allauth I will have to keep everything I need for this.
Also, adding some random info to the JWT is harmful. The payload of a JWT is not encrypted.
sure but since it was sent to a user's email I shouldn't worry about MITM attack as you mentioned that, only the owner of this info will see it if he decoded that and also it is not a critical data to be exposed, just some fields.
Sorry for the relatively late reply.
The MITM attack is not during the server -> client transport (i.e. the email to the user); it's when the client uses the link to go to your website (i.e. client -> server). Additionally, you can really only do GET requests from an email, which is even worse. I understand you don't want to add redundant data to your database, and so you've resorted to JWT.
I think whatever your use-case may be is ok for you, but it's too specific and, personally, slightly not safe since you're putting the token in the URL which means it can probably be re-used during the MITM attack. That means, if a snooper is fast and clever enough, the sign up could be given to the attacker and thus credit card info as well, even if it is from a different service since you're not exactly validating if the user is really the target person or not. What your intended PR implies is that the token is inside the URL, which can easily be picked up by anyone, not even in a MITM attack.
Again, I think you can innovate your methodology to be safe, but it shouldn't be in this package. I try to be inflexible with this package (counter-intuitive for a Python package, right?) because there can be so many security vulnerabilities that arise. I'm not the biggest security expert out here, but there are certain fallacies that can occur if we're not careful.
Sorry for the relatively late reply.
Not a problem at all!, I really appreciate your time and the effort you are taking into this, thank you so much 馃檹馃徎.
The MITM attack is not during the server -> client transport (i.e. the email to the user); it's when the client uses the link to go to your website (i.e. client -> server).
well, that's a problem 馃槄, especially with your later comment as it will be in the URL so using HTTPS has no powers here but it's only at URL so a frontend can pick it, verify with the server and use it in AUTHORIZATION header later fore upcoming requests, so at worst case, if someone stole a token from a header he will access the frontend only but won't go further than that unless he intercepts a full user's session, even an attacker completes the process through frontend he will only complete the signup process for the user he can't do anything with email access and if so then this user's life's is already stolen.
I agree with you that's it's not the safest method and your concerns are valid but I think it requires an exposed user at a higher level which you can't do anything about it, and for sensitive info like credit card data, thanks to payment gateway this info won't be exposed.
I agree with you again, this can be improved and later uses cases to come but I think this can be fine to start with and enhance it over time, there are many ideas, we can combining a token with an allauth link so on user verification we can share this token safely and avoid the mentioned concerns, but for now it still reliable.
Actually, I was going to comment with an update that I did the original goal of my question somehow, I created a token class with a new token_type value and a new authentication class to authenticate that new token_type specifically and I use that authentication class at my view, so with this token and authentication based on that type I did the original goal to specify a token for some parts only. it's turned out to be useful I think
If you'd like to open source some kind of methodology, I'd be happy to take a look at it. For now, I think this functionality has a bit of a security vulnerability and, in general, is too specific to be integrated in my opinion. I'll be closing this. Feel free to keep discussing as I check my GitHub notifications for any thread where there are comments!
Thanks, I really appreciate your help.
I'm willing to contribute with some points which I think will be useful for anyone to more extend.