Authentication with Django and Single Page Apps (2020)
mikesukmanowsky.comI pretty much agree with the sentiment of this post.
I’ll probably get downvoted for saying this . . .
Ever since using JWT’s became a trend, I’ve found that I can’t get a useful answer almost every single time I’ve asked an engineer (or team) why they picked JWT’s over old, boring, and tested sessions for a web app. It seems, just like React, GraphQL, etc., a lot of the industry just love jumping on bandwagons. I see so many companies adopting the new and shiny thing (or the thing attached to a big name) rather than the best tool for the job. Unless I encounter a specific use case that would be best served using JWT’s, I’ll stick with the “old” Redis sessions model.
I guess you’re not a real engineer nowadays if you can’t say that your new app uses insert buzzword or trendy technology here . . .
The use of trusted jwt libraries which outsource registration and authentication has massive benefits such as SSO and reducing the risk of vulnerabilities (user reg/auth being handled by a dedicated party).
There’s no reliance on a database or state management, which can be useful under some conditions.
In my eyes, the problem is reliance of the authorisation header instead of cookies, this has some benefits but is also a massive deviation away from 20 years of websec. Granted all of http spec is a giant nasty hack, so it’s not really jwts fault.
How is your first paragraph a result of using JWT over sessions?
It depends on your definition of session (e.g. if you’re referring to a cookie, or if you’re referring to the use of JSESSIONID or similar). In both cases, JWT is just the token and how and where it’s transferred aren’t spec defined. I susupect the reason for the authorization header to be used is to prevent csrf attacks or to better facilitate SPAs, but I’ve never researched it.
You could technically use SSO with any other auth token, or exchange it during the auth process, but ultimately having metadata about the user in near clear text is useful.
Don’t get me wrong, I think jwt and the rest of websec is a total fucking mess, but jwt is far from the worst offender.
I think the original motivation was tied to when people were trying to build unified APIs for every kind of client: web, mobile, m2m. Cookies and sessions werent always available afaik.
I was under the impression sessions were just arbitrary tokens backed by some server-side logic (or perhaps a database)
At its core isn't it possible to just take an object, encrypt it with a secret, store it client-side somewhere (cookies, localstorage, filesystem, printed-on-paper, whatever), send it back to the server, and it decrypts it?
I don't quite see the difference (or benefits) of JWTs over something like that.
The difference between JWT and session is where the state is stored.
For session, you need a centralized backend to access the data stored in the session. For JWT, you only need to verify the signature of the token to trust the data stored in JWT.
JWT solves the problem of having multiple separated service that need to share data.
Storing state in JWT is easy way to share state, such as user permission, between different server.
But state in JWT can be outdated due to permission changes and you it's not possible to just expire it as it's stored in client.
To solve the problem, more complex auth setup is needed such as using short lived JWT, refresh token, which feels more like a bandaid to make JWT sufficiently secure
Is this really about buzzwords or a lack of understanding of the technology choices?
Django Sessions is equivalent to stateful JWT authentication. Your choice of using one over the other depends on your constraints. But if I needed a session's based solution, I'd probably go with a stateful JWT implementation rather than Django own session. That is unless I am building a really basic app, and looking to get done with it by the next week.
There is no reason to use JWT in this case. Because you trust your session store, right? The only benefit of JWT is when you can't trust the "hands" the token passed through. Otherwise just use JSON...
Not sure what "stateful JWT authentication" is supposed to be anyway...
I think I can kind of understand one advantage of using state-backed JWTs, if I got the idea right: double validation, both client and server side. So client side you get immediate validation of regular expiry and various other attributes without contacting the server. There’s a slight performance boost for the server in some circumstances, traded for always more client side work.
JWT is nothing but a signed JSON. You only sign it because you need some entity to trust it without asking another server. If you need to ask another server, you don't need JWT. And that's what the Django session store is doing. The data isn't stored on the client side.
Another option is a signed or encrypted cookie containing whatever you want it to store. That is similar to a session store but stored on the client side and quite limited in size. Again, that's not really JWT, but you may use a JWT as a cookie if you have to. But JWT isn't encrypted (by default).
I understand what JWT is and I also don’t think it makes much sense to use them in a situation where you’re going to store them as is in the db. I’m just giving an example of what it could bring to the table if you did. Devils advocate and all.
I don't think it's a trend or that people are trying to be cool or something. JWT is like a simpleton version of Kerberos/ActiveDirectory tickets. Having authentication and/or authorization independent of operational systems has significant advantages is architecture and deployments.
I personally feel so validated after reading this post. The past 3 weeks have been me personally, painfully re-deriving this information independently.
JS FE culture is so fucking obsessed with “modern” instead of what works best.
I am using a very similar stack to the author, but I couldn’t find anything as useful as this post.
I feel like an insane person sifting for information for React. So much SEO spam clogging google over useful content like this article.
Yeah. Session authentication works just fine. Especially on the same domain with only one service.
As soon as you need multiple services it gets trickier.
I found those libraries that provide React components for authentication less useful. I prefer to store authentication state in a redux store and manipulate it with a vanilla client library ("ts-oidc-client" or the keycloak specific one).
I'm pretty sure you should also use CSRF protection in POST requests when you do cookie-based sessions.
> JWTs are vulnerable to brute force attacks once intercepted.
On the linked PDF it says:
> Shorter keys can be brute forced.
Yeah... don't use short keys.
And then also misquotes:
> JWT token cannot be invalidated by itself
JWT _can_ be invalidated, you just need to somehow store the invalidated tokens, depending on the use case this can make sense, since the number of invalidated tokens is going to be way smaller.
> But if we need to send this on every request, we need to persist these credentials somewhere. In a native mobile environments, there are secure options, but on browsers we only have localStorage or sessionStorage, both of which are 100% insecure.
The mobile version is not secure either... You need access to the raw payload, so if the app has a remote code execution vulnerability, the attacker is going to be able to read the token.
JWTs cannot be invalidated without giving up the main reason why you would use them: reliance on an authentication server for validating sessions.
A lot of bad arguments against JWT tokens. These items are definitely something you can address with JWT token: * expiration date * invalidation * change of roles or any significant change in user attributes
Moe important, the list of issues would be the same for a session cookie: if you don't expire the session on the back-end or reflect changes in the user attributes, same issue.
Basically, apply the same best practices for session tokens or JWT token and you'll be fine. You can also put the JWT toke in the cookie, it does not have to be stored in the browser local stroage.
What makes this a bad argument against JWTs rather than a good argument in favor of using built-in features of the platform? I agree that any purported issues with JWTs are solvable, but if you don't need the benefits they bring over sessions, why use them at all?
I think the premise of the article, which I wholeheartedly agree with, is that for 95% of software projects developers should choose the simplest implementation necessary to meet the requirements. Sessions come for (almost) free with the framework and most browsers, but JWTs have an additional cost for the problems they solve, which are usually poorly understood upfront.
Claiming that technology A shares the same issues as technology B while technology B is all the hype doesn’t exactly spell out why I should use technology B over technology A.
And this is assuming I actually accept your claim that they share the same issues . . .
I think the selling point for JWTs are that they're a mostly-standardized way to do auth tokens such that you only need to do one very simple and cheap database query (is this a token that has been invalidated but hasn't yet expired) rather than a larger number of database round-trips to implement various authorization checks.
But that's true of session auth as well. The only necessary check is "does this session token exist and have a creation date within the expiry period", which should be a single, fairly quick database query for all but the most extreme "at scale" cases. You should not need multiple round trips to validate that.
The main use cases for JWT are:
* I want to create a session token that I _never_ want to revalidate against a database, and still trust that the only way it can exist is if a user has logged in at some point. This means that you're not going to be and to log a user out of your system, but that can be a valid trade-off in some situations (e.g. low-security applications where the cost of storage is very high, or microservice architectures where the token can be validated on ingress but doesn't need to be revalidated later).
* I want to include role (or other) information directly in the token for the sake of convenience, so I never need to pull information from other sources (in which case there are definitely other ways to do this, but just using a JWT library and using the result as a session token might be easier than the alternatives).
But most cases of using JWT tokens that I've seen has basically involved using them as overly complicated session tokens with all the same problems, and none of the benefits that JWT itself can bring. In that case, you may as well just take the easier option in the first place.
> I want to create a session token that I _never_ want to revalidate against a database
There's the middle ground as well with a long lived authentication or refresh token and a short lived authorization token.
> I want to include role (or other) information directly in the token for the sake of convenience
Once I started seriously developing cloud solutions, this became indispensable. It's not just convenient, it directly reduces expenses.
A refresh/access token flow definitely can help by reducing the number of long-lived tokens floating around, but it's not really related to the type of token you use. The access token could be a JWT token, but it could also just be an opaque random string, or anything else that's convenient to pass around. It just so happens that reducing the longevity of tokens is useful if you don't want to revalidate tokens against a database.
JWT tokens definitely have their place, I don't want to imply that they're useless. They're a tool with tradeoffs like every other tool we use. But I've seen a lot of projects using them without understanding those tradeoffs, and then creating either very inefficient, overly complex, or subtly insecure applications as a result.
> I want to include role (or other) information directly in the token for the sake of convenience
This works up until a certain point. Once you get past simple RBAC to more fine-grained (resource-based) authz, jwts don't scale: https://medium.com/building-carta/authz-cartas-highly-scalab...
I'd also written an article on token authentication for django: https://www.spapas.net/2021/08/25/django-token-rest-auth/ using the REST Framework's TokenAuthentication.
This is simplest thing for most cases.
The session authentication that is proposed in the article is also great but has two problems:
* It will be hacky to implement for mobile apps (it should be possible but would not be something I'd like to do, I had tried in the past and remember that I needed to jump to a lot of hoops to "pick" that session cookie)
* The cookies can't be shared between different domains (cookies be shared the same domain or between a parent and child domain, i.e api.example.com can set/get cookies from .example.com).
So you can use the SessionAuthnentication if your frontend and backend share their domain and you know that your API won't ever be used for mobiles apps. On all other cases use TokenAuthentication.
I don't have experience with JWT Authentication, however I know it can be done and is used be various apps f.e baserow: https://gitlab.com/bramw/baserow/-/blob/develop/backend/src/...
Now… let’s just cover SAML. How do these methods congrue with SAML? I guess maybe all of this (token, etc…) is issued _after_ a SAML “handshake” is complete?
This works for quite a few simple and not so simple usecases. You should look into using proper oauth2 or oidc when it makes sense to do so.
For example if you want multiple services or sites to use the same login/token. There is keycloak and django-oidc-provider. Those handle users and tokens for you.
> localStorage or sessionStorage, both of which are 100% insecure.
This makes me doubt everything else he says.
I'll admit "100% insecure" is perhaps a bit hyperbolic, but they are insecure from the standpoint that any other script executing on the page can examine the contents of either localStorage or sessionStorage and then try to perform a brute force attack.
is there an implicit assumption that we're operating on the same domain here for the frontend and backend
I'm not sure that it's so easily handled if you're working from www.foo.com and api.foo.com
The main assumption is just that you have a single API domain. The fact that the frontend and API may be on different domains doesn't impact this recommendation.
If the API tries to set an HTTPS-only session cookie on api.example.com, the client/browser will simply forward cookie that on every request (including requests made on behalf of a user like a frontend calling fetch()). You can try this yourself, or see it happening in the Github example linked in the post.
If you had backend APIs supported by different domains (api1.example.com and api2.example.com), things do get more troublesome. You could still configure the cookie domain for .example.com, but then you're sending the session cookie along with any request to any example.com subdomain.
hahaha, let's go back to 1998 and use cookies and sessions for authentication.
seriously, use time-based and hmac-based one time passwords. combine them with the user's email, which is a strong guarantee for identity and uniqueness. if the user chooses to use a disposable email, it becomes their problem, not yours.
Huh? Sessions and TOTP aren’t mutually exclusive. In fact, they work quite well together.
And since when were cookies and sessions 1998-level technology? That’s ridiculous.
Should they have said 1994-level? :P
I don't recall setting HttpOnly cookies in 1994...