Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: What's the current sentiment on JWT for stateless auth tokens?
373 points by gkop 38 days ago | hide | past | web | favorite | 227 comments
HN threads from 2016-2018 complain that JWT gives us too much rope with which to hang ourselves, and some advocate for alternatives like PASETO instead. Today it's not clear however that any alternatives like PASETO have crossed the threshold of adoption to warrant preference over JWT when choosing a stateless auth token solution.

What solution would you choose today for stateless auth tokens?





The first comment from 11 months ago is wrong.

>JWTs are useful, but there are a few things that are not immediately obvious. >1) They are signed not encrypted.

This is not true. JWT's can be encrypted, signed, or both (nested). One might first look to see if this misconception is the fault of a poorly written spec, but it's literally in the second sentence of the abstract and plain as day in the introduction. People are simply not reading it.

https://tools.ietf.org/html/rfc7519

It drives me mad when people refer to JWT's as basically JWS's, ignoring the rest of the JWT standard and JOSE. The fact that these misconceptions continue to be up voted shows the broad misunderstanding of the standard.


Or, they highlight that the spec is overly complicated. And a complicated spec is very challenging to implement both correctly and safely.

EDIT: Case in point, all the disagreements and conflicting best practices throughout the threads on this topic.


I think complex specs are fine. Complex and under defined spec are a nightmare. I had to work on a WebAuthn project recently. That spec is rather complex, but makes perfect sense after you take the time to understand it. More recently, I had to answer a very simple question about X.509. After spending half a day reading the RFC (and about half a dozen other dependant specifications), I gave up and concluded that the question did not have an answer, the best I could come up with was some general graph theory principles relating to DIT.


> I think complex specs are fine. Complex and under defined spec are a nightmare.

Perhaps, but in practice there generally seems to be a correlation between complexity of a specification and holes in the specification (e.g., html, pdf, etc.).


Complexity, whether necessary or not, makes it harder to maintain a spec. But I’d argue that it’s other issues, whether they’re symptoms of complexity or not, that make it hard to implement. Some things have to be complex, but they don’t have to be full of problems.


Thank you for this comment, I feel very much the same way. The JWT spec isn’t wonderful and it is complex however, having spent the early part of my career working with SAML, SOAP and xmldsig in highly trusted environments it’s actually a breath of fresh air. Those specs were littered with security flaws.

That said, the class of attacks that relied on the attacker modifying the header of the JWT could have been totally avoided by including the header values in the signature calculation.


Or you work with an opaque token. Then no one knows what's inside...


In my experience, don't use JWT's for carrying application state.

In more words, if you need to carry application state and you haven't outgrown the simple database cluster, then just use the tried and true formula of carrying state via SQL database. Pretty much every engineer these days understands how the architecture works, it's fast and reliable due to the sheer maturity of the components involved, and you don't add cryptography and maliciously crafted input attack surfaces to your auth system (see the recent talk at BlackHat 2019 about Outlook accepting unverified auth tokens). In this case using a predictable and mature approach is a good thing.

On the other hand, JWT's work well for (1) federated authentication, though revocation is still hard, and (2) carrying authorization claims. The reason is that these aren't state in the sense of application state. They're attestations of an existing fact and you're using the signature mechanism to let one service trust another without having to talk directly to each other or to protect any shared secrets. You need to send signed payloads containing user ID's and lists of claims anyway, and JWT's meet those requirements pretty well.


> though revocation is still hard

My understanding was that they're supposed to only last on the order of 5-10 minutes, with a fresh one created for the next set of requests instead of reusing one JWT for days. Is revocation still an issue with the expiry so short?


The expiry is one of the tradeoffs to be made. We've also used a kind of emergency switch in the system that expires all tokens that are older than a timestamp system parameter. All this does is force token renewal so legit services continue chugging along while the forbidden will be stopped at renewal time.

In general, I tend to look at token trust as a decaying function of time, irrespective of signatures.


That's what my philosophy is (15 min max) based on my understanding, but in many places I've seen, people set the expiration to something insane like 5 days.


No, you just have to always keep in mind that if you misconfigure your application to sign a token with no expiry, you will never be able to revoke it. Period.


I mean, this isn't really true though. If you forgot, you can have your JWT middleware check and if the field is missing, revoke it. There are tons of workarounds here and I have implemented them in several consulting roles. I personally always put a created and expires timestamp, bit if they aren't there, there are ways to fix the situation easily.


Hmm, couldn't you also rotate encryption keys once a day/week, and keep the last one or two available, and if decrypting with the current one fails, try a prior one? Eventually, that token will no longer be readable. It should only ever matter in the weird case where your expire time is screwy, but you've put a bound on how long a token can survive no matter what.


No, not period. If you're using an API gateway (which you should be), you can check against blacklisted tokens. The `jti` is your unique token identifier. As long as you have a way of maintaining a blacklist of tokens, this is trivial.


One of the major points for signed tokens is stateless operation. If you need to maintain a blacklist, you lose that, and the question arises: why not just have a whitelist of tokens/UUIDs/... to begin with?

This is why people suggest "short tokens" as a work-around, accepting a 5min window of unrevokable tokens as trade-off.

Or, one has to justify that a blacklist implementation is significantly cheaper than a whitelist one. But is it, really?


If you have a sole identity provider, or token issuer, the blacklist can be quite simple to maintain. Plus, with short token life spans, you probably aren't revoking tokens very frequently.

In my experience, usage of the black list occurred with two use cases:

1) Invalidating all of a user's tokens 2) Alteration of claims that a user's JWT should have

As far as your applications go, you still have the benefit of not having to bother with checking the state (this is already done by the gateway).


That depends on the size of your system. I've worked on systems with so many servers that it would be cripplingly difficult to quickly propagate all whitelist updates to all servers in all services that would need to verify against the whitelist, and the alternative of doing a lookup against the whitelist for every request would be extremely expensive.

On the other hand, you generally expect that you rarely ever need to actually revoke tokens, so you can naturally expect that a blacklist will change infrequently and will remain tiny compared to the whitelist.

As the size of the service grows, most people converge to a fast train/slow train design: the whitelist is big and is accessed infrequently, and the blacklist is tiny (goes obsolete within the TTL of the whitelist query) and can be pushed out quickly.

If you're one of the tech giants, it's not slightly cheaper, it's a lot cheaper.


Or more simply, just check that the JWT has an expiry date, and it's a "sane" one - you can do both these checks without requiring a blacklist.


yep. however, the token is then not "stateless" anymore since you have to take note of the blacklisted state.

however, once you really care about the security perimeter, i think the requirement/wish "stateless" must be dropped.


You can add this type of thing to your validation code: exp has to be set, can't be in the past, and can be no more than x time in the future (where x is somewhere between what you're actually signing now and the largest value you could ever conseivably want to use without having to push an update to this app).

One of the biggest challenges with using JWTs is really all these gotchas that you have to think about. This isn't dissimilar to the infamous `alg:none` fiasco. In this case, it's not really about protecting against an attack, but forcing the developer on the other end to think about it. Their quick initial code that doesn't bother setting exp or has it hard-coded to 1000 years in the future will never work, so it had less chance of being left in to cause a security issue later.


All such tokens would be effectively revoked anyway since you check the iat timestamp (or lack thereof) at the authorization point.


Yep, and that is what we do. But we also check to ensure that a secure cipher was used, not none. Many of the "JWT is insecure" type articles (and real vulnerabilities found) were based on the fact that people just used dumb defaults and didn't secure things. So the argument is really that JWT is a footgun without some adult supervision.


When verifying the token you could just check if exp is set and if the lifetime of the token does not exceed your limit.


> In my experience, don't use JWT's for carrying application state.

Wasn't the question about using them for auth, not application state?


That’s a good point, I was specifically addressing “stateless auth” as an issue of persisting authentication related state on the client side vs. the server side, as opposed to “the authentication system has no state”, since that’s usually the framing of the question.

In general authentication/authorization isn’t just a binary in or out. For example, you might have to reauthenticate to access password or 2FA settings in your account, so now you have multiple authenticated states. So I made my comment from the assumption that you probably need to decorate the user’s authentication “state” with extra information for your service. In those cases, I was suggesting to only carry that info in the token if you need to cross service boundaries with it, such as if you have multiple front end micro services operating on independent data stores.


> Wasn't the question about using them for auth, not application state?

Whether a user is auth'd or not can indeed be a form of application state


JWT were aiming to be stateless, yet they cannot be stateless if you need them to be revocable. If you keep using plain sessions you got a SPOF, but you are immune, for a whole lot of consistency and cred management problems.

http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...


To be clear though you generally set the validity to be relatively short and refresh often, so that way you don’t have to worry too much; you can then “revoke” a token by refusing to refresh it.


But then it isn't stateless, right? Because you need to know which current tokens are unrefreshable.

To me, JWT works great for things like physical people getting access to a physically isolated building, like a military research site that's disconnected from the internet. Some secure other place holds the private key and signs the JWT token then you can wave in anywhere you need to go until the validity period is up. Then you have to go back to the secure place to get a new key, verifying the same way you did the first time and the secure place can broadcast its public key to the sites you want access to. It's essentially a great mechanism where you don't have constant communication links.

But I just don't get it for servers. Also, everyone considering using a JWT token should know about the "algorithm none" setting and ensure that their stack isn't vulnerable to it.


But then it isn't stateless, right? Because you need to know which current tokens are unrefreshable.

You only have to involve state in 1 request every 10-15 minutes upon refresh. All other requests can be done without having to check for revocation. This is a pretty nice real-world tradeoff.


While I haven't taken the leap to using a JWT in any of these cases yet and am a little bearish on them, I do also feel like the baby goes out with the bath-water, here.

Nothing about adopting JWT says you can't still use a full auth check for state-changing endpoints, and use a stateless session to shave some evaluation time off of frequent read-only requests for data that isn't terribly sensitive.

You probably shouldn't do it lightly or prematurely since it makes it harder to reason about authentication, but I think it's worth considering whenever you're optimizing.


Actually creating a JWT token is not stateless, but at least your architecture is not centralized. If your auth server goes down, that’s a problem no matter what.

A common validity period is around 1 hour, which is good enough that most users won’t need to refresh very many times per day.

Also: you don’t necessarily need to track tokens that aren’t refreshable. A typical architecture might have a timestamp stored per user that specifies the earliest token creation date that’s valid for refresh. That way you can invalidate all tokens without needing to ever store them; something you might need to do for password reset, for example.


> A typical architecture might have a timestamp stored per user that specifies the earliest token creation date that’s valid for refresh.

Never worked with JWTs but still, can't believe I didn't think of this. It seems like a fairly elegant way to revoke things where your only stored state is a timestamp.


> A typical architecture might have a timestamp stored per user that specifies the earliest token creation date that’s valid for refresh.

There's a race condition there, though. If you have a revocation event and set the timestamp, it's possible that an in-progress refresh might have a timestamp just slightly after your stored timestamp. Then you have a rogue JWT that can continue to be refreshed.

Clocks are sometimes not in sync with millisecond precision. Databases don't replicate instantly. Distributed systems just don't work that way.


You could instead store a "generation", and increment on revocation events


>algorithm none

I just read about it on Auth0 [0] and facepalmed. Then I went to node-jsonwebtoken to ensure they added an ‘algorithm’ argument to a ‘verify’ call and you guess what [1].

They added ‘algorithms’, but into ‘options’, so that naive user like me could just skip it and stay open to attacks. That’s auth0’s own module, btw.

Thanks for pointing that out!

[0] https://auth0.com/blog/amp/critical-vulnerabilities-in-json-...

[1] https://www.npmjs.com/package/jsonwebtoken#jwtverifytoken-se...


Use a proper JOSE module like https://www.npmjs.com/package/jose that uses native node's crypto key objects (ergo not leaving you vulnerable to HMAC based validation bypass) and doesn't even implement "none" algorithm.


You always have to authenticate the JWT anyways to verify data hasn't been changed. The stateless part is the fact that the sensitive data is stored in the JWT itself instead of retrieved from a DB. As long as you can validate the HMAC / Signature then you can assume the contents of the JWT haven't been altered and can therefore trust the client's data.


Authenticating JWT isn't checking the JWT against an external server.


How can you validate that it was properly signed without comparing it to the entity's public key?


Why would you need to make a request to another server for this purpose?

Hint: you don’t — but I figure taking the Socratic approach may be more convincing.


You could pre share a public key, but a common way is to use the discovery mechanism in OIDC to automatically manage key distribution/rolling.


Because else how do you handle key revocation?


Which is the point of the post.

If you need to do revocation you need to call an external server.

If you need to call an external server why do JWT?


Because pinging another server for a few bytes of data and offloading that working memory to the client side is much more efficient than storing the session data in server memory in between requests. Furthermore a single coordinating key server can work in conjunction with many micro-services to simplify architecture, security, and hardware requirements.


Which still leaves a window for JWT to be re-played. If you store something valuable, like # of credits left on a users account, the user can spend some, and then re-use an old jwt to spend again.


It’s stateful for the auth service, stateless for the relying party.


The link seems to think this is impossible but I don't understand why.


Don’t see the word “refresh” in there. Guessing they are assuming you use JWT identically to how you might use a session cookie, which is not how I’ve ever seen it used, personally.


In the diagram in the bottom right they seem to think you cannot revoke the long term token. I agree that it doesn't align with my understanding of whats capable with oauth2 style tokens, for example.


That box should probably read: “You can't revoke the long-term tokens in a stateless manner, so supposing you truly manage ZERO state, then you're back to square one.”

That seems like the charitable interpretation. Note that the author’s point has not been that JWT is useless, but rather that trying to use stateless JWT for sessions is a bad idea.

If the point of using for JWT was to avoid all server side state in authn/authz flow — which is the case for many, many developers, and that is the intended audience of the author’s post — then one of two things: you either contradict yourself and implement server side revocation for refresh tokens, or you let the lifetime of your sessions be the same as the refresh token — and in the case of refresh tokens with no expiry, the user (or anyone with their refresh token) can stay logged in forever.


The state necessary to revoke JWTs is easier to manage than session state.

1. It's small (id per revoked, nonexpired JWT)

2. It isn't sensitive, nobody learns anything by seeing the list of revoked JWT ids.

3. It's monotonic. You just maintain a set that you add to. You can clean up expired tokens if you want but that's not necessary for correctness.

These three items make bugs less likely and make it easier to manage consistency when you can't afford to use transactions everywhere.


The monotonicity alone makes the required state _very_ easy. Adding (timestamp, jwt_id) pairs to a monotonic set which you occasionally switch over to a second, fresh set (checking against both, adding to one) requires zero coordination in the critical path. The only coordination required is about when the sets are switched, where you need to ensure that you leave ample time for old JWTs to expire before clearing the corresponding revocation set entries.


Rather than blacklist JTIs would it not be simpler to keep a valid after date on the user? Any token issued before that date is not acceptable


Then you can't have users sign out of device A while remaining signed into device B.


Revoking per-JWT requires state per-JWT, but you can get away with globally-revokable tokens ('logout everywhere' functionality) with a single nonce-per-entity instead of a nonce-per-JWT, which is a bit nicer.


Right.

One point to keep an eye on is, if you are using (e.g.) AWS API Gateway with a custom authorizer, it caches the authentication response for a certain time.

This is not a problem of jwt, but makes the 'logout everywhere' and every black and whitelist need some more time.


You can revoke all JWTs by changing the signing key on your server. All previously issued JWTs will be invalid.


Sure, but that's the nuclear option. Imagine if Facebook did that every time one account was compromised or they fired a staffer?


Facebook as the largest userbase of any application in existence. Its needs may not equate to your needs. For example, an enterprise b2b app with a few hundred customers could probably "deal with" the nuclear option.


Man I'm not sure I agree with that. Blowing up a bunch of your clients that rely on your services randomly because one account was compromised is not acceptable for really any professional business regardless of the number of clients.


"Blowing them up" is a mischaracterization of the situation. You are not mutating their data. You're only asking them to login again.


Actually blowing them up is a completely appropriate description if you have any non-human's relying on JWTs.


Applications that use authenticated services need to handle being the authentication being revoked. That's fairly obvious regardless of how it happens. If your app doesn't then it's broken.


I'd be pretty upset if I got kicked out of a work app every time one of a few hundred companies fired someone (or even had to do a password reset for a security reason).


A multi-tenant system could use a different key-pair for each tenant and only revoke the one for the compromised entity


You don't use a tool for managing your passwords? That's built into most browsers these days.


The issue is not remembering the password.

The issue is I just logged in 90 seconds ago, typed up a few paragraphs, and when I go to submit I have to do it again, because someone in another company using the tool got phished and needs a new password.

It's frustrating enough when Gmail makes me put my password in after 30 days and I just clicked into an email I wanted to read. Having it happen all day long would make me want to kill someone.


I wasn't suggesting mutating the signing key multiple times per day or once per 90 seconds. At that point, you might as well just have a very early JWT expiration timestamp specified.


The comment thread started with "if you need them to be revocable". Any tool with "a few hundred [enterprise] customers" needs to be able to revoke access for terminated employees, compromised accounts, and the like, and those revocations will occur frequently and in many cases need to have immediate impact.


So now every user has to log back in again every time you upgrade your server, simply changing the signing key or when your backend gets compromised.

Additionally JWEs don't support forward-secrecy meaning that I can MITM and collect all JWEs passing through, find the key in your compromised backend and decrypt ALL these collected tokens.

No need to do this with JWTs since they're unencrypted by default.


Your server shouldn't be getting compromised on a regular basis. The occasional black swan event occurs, and sometimes you have no other choice.


Users get compromised though and will want to revoke sessions. But this is why you just set a time limit on your JWT, so that they can revoke and within N minutes the old sessions will die. Just keep N low.


It's not stateless, it just uses a blacklist instead of a whitelist.


An approach I have used in the past is including the optional `kid` header. The key id can be matched to the appropriate key needed to verify the signature. If you associate every user with a key, that means you can also delete any key and thus revoke the jwt of a particular user.

In this case obviously you have a persistence layer for the keys and can't claim to be absolutely stateless.

https://tools.ietf.org/html/rfc7515#section-4.1.4


> If you keep using plain sessions you got a SPOF

What single point of failure is inherent with plain server side sessions and a session cookie?


The server.


... ok so assuming you then don’t have a HA environment (Really!?) if the server is down the sessions and the tokens are irrelevant because there’s nothing being served and nothing to use/check them and provide back resources.


You don't have to store them purely in-process. The session data itself can live in Redis. You can scale sessions beyond a single web server.


So wait, why does the refresh token not work? Why wouldn't you verify user permissions when generating a new access token?


Remember that the author’s intended audience is developers who want to use JWTs specifically to avoid server side state in the authentication/authorization logic. The point is that, if you want to revoke the refresh token, you have to maintain a blacklist somewhere, and now you’ve contradicted your original goal of avoiding all state, which means that choosing JWT for this particular purpose was illogical, and probably warrants further thought. (TBC, in logic, if your hypothesis leads to a contradiction, then your hypothesis must be false, which is exactly what we’re seeing here in this scenario.)


I don't see this as rendering JWTs useless, though. If you have a 10-15 minute validity time, and you only check for revocation on refresh (and perhaps, optionally, on sensitive operations), then you don't have to check the the revoked-tokens table on every request, which means your database doesn't have to scale up nearly as much.

And since the IDs in the revoked-tokens table only matter for the duration of token validity, you can prune the table pretty often, meaning necessary space needed in the DB is relatively small.

It's certainly more complicated than just dealing with stateful session tokens, so you might decide that the trade off isn't worth it, though.


I disagree. If you make an API Gateway responsible for that concern then it's the only thing that needs to maintain state. The blacklist itself probably kept in something like Redis and the API Gateway just checks against it. Then all downstream services get the benefit of JWT in a "stateless" fashion as advertised. That is, none of the downstream services know or care about any state. They get a request with a JWT and if it has the right claims, it services the request.


> revoke the refresh token, you have to maintain a blacklist somewhere

Store CreationDate in the refresh token. Store RefreshTokenInvalidBefore in the user record. When refresh is attempted, if Token.CreationDate <= User.RefreshTokenInvalidBefore, reject the refresh. The only drawback here is that it would sign the user out on all devices. However, assuming this is because of a password or token compromise, that's likely the least of their worries.


You need a really short refresh cycle for that to be useful to customers. If your customer FooCorp fired Bob, they want to be able to disable his access immediately, not in 3 days when his access token next needs a refresh.


15 minutes seems to satisfy most use cases.


For more secure apps we use single use tokens and expire within a very short period of issuance, i.e. invalidated upon use, and exp (expiry) set a few seconds after iat (issued at). Not optimum for performance because you need to get a new token with every request, but means you never have to worry about having to revoke them. Not heard of anyone in the finance sector using JWT for session management, but that's not what they're for.


Of course you can. Put your auth behind a sane CDN with TTL of forever, the kind that allows tagging an arbitrary number of objects with a single tag and allow to purge objects matching a tag in a single call within a few milliseconds.

Tag them with sec-uid, sec-uid-dev-id, sec-devclass, sec-we-dont-like-user etc to group them together.

Want to kill sessions of just user uid? Hit a purge endpoint that purges all objects with sec-uid. Want to kill a session for a specific user for a specific device? Hit the purge endpoint that purges all objects with a tag sec-uid-sec-devid. Want to logout all users on an iPhone because "oops, security bug in a version we rolled out"? Hit an endpoint that purges all the docs tagged with sec-devclass of an iphone.

You don't even need to remember tokens you issued.


You are describing stateful authentication.


Nope. You don't even need to know you issued those tokens or their state. You don't ever assert a state or check it. You force its recompute via cache bust.

You don't need to know a token of jakelazaroff. You don't need to know its state. You don't even need to know if it exists.

You know that a token associated with jakelazaroff iphone if it exists will have a tag jakelazaroff-iphone because every token associated with <user> iphone will always have a tag <user>-iphone since you always tag a token of any <user> on an iphone with a tag <username>-iphone when you issue the token.


I don’t understand your explanation, but the words that you’re using (e.g. “purges”, “cache”) indicate that you’re describing a stateful system.

Imagine you’re writing your API on AWS Lambda, so memory and the file system are not shared between requests. A client presents your API with a token. Using no external resources (no database hits, no network requests, etc) how do you determine the veracity of that token?

Edit: I think I understand what you’re saying. Using a CDN that will let you “tag” resources and conditionally provide access based on whether HTTP headers match those tags, you issue tokens to clients and add or remove tags to give and revoke access. The CDN determines whether a given tag can access the resource, and your application doesn’t worry about authentication.

This is still stateful. The tags are the state!


> Using a CDN that will let you “tag” resources and conditionally provide access based on whether HTTP headers match those tags, you issue tokens to clients and add or remove tags to give and revoke access.

The tokens are the resource.

Your auth is what tags the tokens. Your auth is the only entity that is able to compute the tags that would match the criteria of the tokens to invalidate them.


Correct. I can assert a validity of the token without touching any database. Not only that, but the API does not even need to compute what it is. That's what makes it stateless.


It’s not stateless, you’re just trading a database for “tags”. This is exactly the same as if you just placed a stateful authentication service that checks a database in front of your API. How do you think the CDN works?


I guess the idea is that it's "stateless" in the sense that you don't have to maintain the state yourself, and the CDN will do it for you? I mean... okay, I guess the point of being stateless is that you don't want to deal with the scaling and reliability issues around storing state, so pushing that responsibility onto a third party (whose uptime is required to get to your site anyway) is "stateless" from your perspective.

I certainly wouldn't call that "stateless", but it does fulfill the main reason why you might want to be stateless, that you don't have to maintain any state yourself.


Exactly — it's basically the same as writing your own separate authentication service, or using Auth0/Cognito/Firebase.


The reason why it is stateless is because there's no state associated with a token that I have to maintain.


You do not maintain the tags. You compute the tags and you do not care what so ever if the tag ever matches anything.


It doesn’t matter who maintains them — the tags are still state. It may be stateless from your API’s perspective, but the system as a whole is still stateful.


Today I would still use alternatives like PASETO [0] or Branca [1] tokens which the latter is a fernet-like secure alternative to JWTs.

The problem with JWTs is that they are discouraged by many cryptographers due to their "cryptographic agility" and provide a mixture of protocols which includes weak ciphers and configurations to shoot the programmer in the foot. Why include insecure ciphers in the first place? The mentioned alternatives stick to a single cipher for its intended purpose.

JWTs are inefficient for performance and are bloated in their data structure and have a performance hit when you parse the token to extract its properties compared with the same operation in other alternatives (simple session cookie require zero parsing and thus is faster).

And using them for sessions poses it own pitfalls [2] which you are better off with the alternatives or plain old session cookies.

They say that JWTs are "good when used right" but with those above footguns, that's like saying C++ is safe when used right, rather than having safe defaults.

[0] https://paseto.io/

[1] https://branca.io/

[2] http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...


> JWTs are inefficient for performance and are bloated in their data structure and have a performance hit when you parse the token to extract its properties compared with the same operation in other alternatives (simple session cookie require zero parsing and thus is faster)

While a session ID doesn't require parsing, it is useless on it's own; a fairer comparison would be: time to validate and parse/deserialize a JWT vs time to retrieve session data from the backing store (e.g. database, redis).


Another interesting one is Macaroons [1], although the implementations have not had much activity since 2014.

[1] https://ai.google/research/pubs/pub41892



This is very interesting! Although it is very much a draft in progress.

It also references a new one I haven’t seen before, https://vanadium.github.io/concepts/security.html


I've come across PASETO before, but never heard of Branca before.

I certainly like the lack of footguns in PASETO, but I'm curious about how much real-world adoption it (or indeed other JWT alternatives) really have?


Popularity is never evidence of rigor or soundness. Lots of people do dumb things like lemmings.


While I like the sentiment, it's a little naive; I want to use something that's got real community traction, and is going to be around long-term.


Oh! I love fernet (not the library, but the alcoholic beverage)


I think, philosophically, they're interesting, but in practice the statelessness ends up not being a huge benefit.

In the few deployments I've seen, you end up observing this pattern of, first, not enough information is placed in the JWT to eliminate stupidly common database calls on every request (example: "the JWT contains a UserId, but we need a RoleId that we store on the user document to determine their authorization level. for, like, every API call.")

Ok. So, two options: you can give up, admit JWTs were a weird choice, and just do everything possible to make those common database calls fast (which is a very solvable and scalable problem); or, you can go down the rabbit hole. Lets put a RoleID in the JWT. But, really, a RoleID doesn't tell us anything about the user's authz; it saves us one table join, but we need the underlying permissions/claims/authorizations/etc. We could put those in there.

In other words, "stateless" is a misnomer. You just moved the state; from a single, consistent, and scalable database, to a "distributed database" where every node is a JWT held by your users.

JWTs are probably fine. I just think the most claimed benefit they offer is mostly overblown. But, in the 2% of use-cases where that benefit could be exploited to the maximum, they're worth using, and they're also "fine" in the 98% of other use-cases.


You could keep the role information in memory pretty easily though. A lot setups don’t have that many roles (even big enterprise deployments I’ve seen tend not be more than 1000, and that’s pretty extreme).

The roles per user and auth of the user on the other scales linearly with users and is harder to keep in memory.


I am fine with using JWT-alikes for bearer tokens. Good for one request or a very time-limited duration.

Some use cases:

1) Sign In with Google gives your browser a time-limited JWT that it then provides to the application you're trying to sign in to. The application's server checks the validity of the token (without having to contact Google, except for daily key refreshes) and can upgrade that to a session for your browser using its normal session-management machinery. The idea is to transfer a piece of information between two systems (Google and the server app) through an untrusted system (your browser). For that, the JWT works well enough.

2) I wrote a proxy to convert one's external SSO session cookie to a JWT internally. That ways apps can check the user without having to make any requests to a user service, and can propagate that safely to other backends. It was very time-limited (min(RPC deadline, global deadline) + a few seconds of slop in both directions) and never exposed outside of an internal network.

Some anti-patterns:

You don't want to store sessions in a database, so you store the state in a JWT that the user stores in their browser cache. This has the problem that you can't revoke the token when the user is fired or gets hacked, which is a big problem. (You can of course add a database that only stores revocations... but you now have that check inside your request path, so you should have just done it the other way around and saved the user from having to send you a giant cookie on every request.)


> I wrote a proxy to convert one's external SSO session cookie to a JWT internally. That ways apps can check the user without having to make any requests to a user service, and can propagate that safely to other backends

I've done something similar, to "terminate" Windows authentication at an API gateway, so downstream services can run on any platform, and don't have to deal with the vagaries of Windows auth.

JWTs worked very well for this use case (obviously any other kind of token would have worked too, such as PASETO).


Add 'issuing date' to the token, revoke all before a given issue date by a single date field in your auth table. Effectively clears all tokens. Would that work, if done up front?


You have to check this date on every request.

I think, on some level, you can make a revocation database scale better than a validity database. You store the revocation in some durable datastore. Then you have some service in front of this that calculates the entire set of revoked tokens, and pushes that to each auth gateway. Then the auth gateway can check if a token is revoked without putting any load on a database. It has to do this for every request, but you can scale the auth gateway horizontally rather easily.

(When an auth gateway starts, it gets a copy of the full revocation table again.)

On some level, you do need the ability to push revocations to your auth server, because when you hit the kill switch on a session, you really want it to close all their open TCP connections (HTTP/2, Websockets, SSH, etc.)

Having said that, starting simple is the best approach. I used MySQL to store sessions at my last job. Every request went from Envoy -> ext_authz -> authentication/authorization service -> MySQL and this whole flow added only 1-3ms of latency per request. Someday you will hit scaling limits of using MySQL for this, but it's a long way in the future. Do the simple and reliable thing first, then make it complicated later.


I'm bearish on JWTs in many cases for reasons enumerated here and elsewhere.

But I do use them in one place with a revocation scheme similar to this. On init, instances build an in-memory bitwise blacklist that token IDs are checked against.

This arrangement probably already implies this, but just in case: this is at a very small scale where the complexity of pushing revocations isn't justifiable, power granted to the token-holder is limited and easy to clean up if misused, and revocations are so rare that restarting instances to update the blacklists is tolerable).


> 1-3ms of latency per request.

Is that a big problem in practice? I always wonder about the tangible benefits of this approach.


Yeah, I am quite OK with these numbers. The sessions-in-a-database-table is fast, simple, and accurate.


Optimization if revocation isn't common: - Keep a last revocation time somewhere that is cheap to frequently poll (Redis) or broadcast it to all your nodes - Keep a revocation table - Use iat and exp with your JWTs - On revocation update the table and the last revocation time. - On validation, only check the revocation table if iat < last revocation time. If you have to check the table, reissue the JWT with the same claims and exp but updated iat.


Every time you have to revoke a user's access, the entire organization has to log in again?


Per user field.


You've given up the benefits of a stateless token if you're looking in the database to check it's state all the time.


That is always the key point it comes back to. Thanks.


JWTs aren't just bad because they embody poor cryptographic engineering, but they are also bad because stateless authentication in itself is poor security engineering for almost every application.

> What solution would you choose today for stateless auth tokens?

Issue token (=random 256 bit string), verify against cached database. If you believe this to be a significant performance issue, you are very likely wrong.


It's kinda funny how some people think that this would be a performance problem, but it's something that computers and programming languages are specifically designed to do really well.


KV lookups with no dependent data are pretty much infinitely scalable with modern systems.


It’s not the lookup that is the cost but the network call.


That's darn cheap in modern systems, and most are doing a plethora of network calls per request either way. .5% overhead won't matter for a long time.


If you like Ptacek, read this: https://latacora.micro.blog/2019/07/24/how-not-to.html

But more importantly, why do you need stateless auth tokens?


> why do you need stateless auth tokens?

So you aren't hitting some validation server on every HTTP request to validate a token. Said validation server adds a very nasty point of failure & potential for page latency.

Going mostly stateless via JWT lets you balance the desire for fast token revocation times and load on your auth server. Even if your revocation time is a minute, meaning your clients need to refresh their token once every sixty seconds, that can still substantially lighten the load on your auth server. High security stuff can always validate the token on every request but stuff that doesn't matter as much can have a much more policy.


> So you aren't hitting some validation server on every HTTP request to validate a token.

This is not a real problem for the 99.9% case and you can add another nine if you aren't defaulting to "extreme conservative"on your risk analysis. A session token lookup in a Redis or a Postgres is completely trivial and cheap.

("But my auth service is over a network link," cries the architecture astronaut, who now has more problems and fewer solutions. Build simpler things, you almost certainly don't need more complex and you can build better solutions than JWT when you need them.)


The only architecture astronaut around here would be the one who makes up requirements like “needs to validate every request against a backend auth server” and “network calls to an auth server are cheaper and easier than local token verification”.

The crypto used for JWT not some brand new space age thing. Public key cryptography has existed for decades now.

JWT is a very elegant solution to the problem space once you understand what it does. I get the feeling the actual architecture astronauts are the folks around here dismissing it.


Implementing a logout-all-sessions function is not the behavior of an architecture astronaut--but building distributed auth because you footgunned yourself with microservices certainly is. And to implement that with JWT you aren't doing local validation because you can't do validation without implementing revocation and you've devolved back to checking a store for validity anyway.

That might not matter to you because you might not work on things that matter and that's fine. If what you work on doesn't matter, do whatever you want, but if it does, don't use JWT.


I worked at a place which had local server in-memory cache talking to local Redis cache talking to shared Redis cache talking to MySQL session tables and the traffic to the actual session tables was still heavy enough that they had to sit on their own DB cluster. Switching to signed session cookies encoding a few bits of information (or JWTs if you prefer a mad complicated outsourcing) would have been a huge win on that point.


What sort of traffic levels were you hitting?


> "But my auth service is over a network link," cries the architecture astronaut, who now has more problems and fewer solutions.

There are at least a few cases where a central auth service makes sense. Gov't here uses it for example, so that the services from all the various departments and whatnot don't have to roll their own auth.


Certainly there are! Nobody's saying otherwise. But they are few, they are far between, and if they manage anything important they should be using OAuth to validate identity and provide credentials that the services check on-request.


That's exactly what they're doing, using JWT for OAuth 2.0 bearer tokens.


Which is not checking on-request. If a token is invalidated, it must be rejected inline. Otherwise, you're just fucking around. Which, if you aren't doing anything important, sure, fine--the problem is that most of the folks who leap to poorly-specified jank like JWT are doing important things with tools they don't understand and the people that are impacted are downstream of them.


Not sure what you mean, and I might have explained their setup wrongly, I'm no expert in this.

I ask for an access token from auth server, I pass access token to API server, API server optionally checks with auth server if token is ok, or if token is self-contained, verifies signature, checks expiration time etc.

Obviously self-contained tokens cannot be revoked, they can only expire.

For this service, JWT is used when requesting an access token, and as an access token for self-contained tokens.


Network latencies and round trips will almost certainly be the primary source of performance issues in any larger microservice environment. If you don’t need that level of scale, that’s great. But once you do, JWTs are perfect for carrying information across the network that may be verified without extra network calls involved.


> But once you do

Sure.

The vanishing minority of projects ever get to that point.

Didn't we internalize YAGNI literally a decade ago?


A vanishingly small minority of projects need the ability to instantly revoke a token on every HTTP request. And even then there is nothing stopping you from doing so with JWT.

Know your facts. And YAGNI and all that...


"Logout all my sessions" is basic decency towards your user.

I get that it's hard with your bloggable thing, but it's better for the people on the other end.


JWT is much simpler architecture than looking up session tokens every request.


Until you need revocation.

Which is to say until you need a working authentication system.

JWTs solve a nonexistent problem in the 99.9% case but are so very, very bloggable that they've brainwormed the population but good.


It’s a trade off dude. You trade off the ability to revoke a token instantly for fewer backend calls. For most parts of your site (99.9%) that trade off is fine. For the parts where it isn’t fine you... call the auth server every request.

JWT doesn’t mean you give up anything....


That's demonstrably not true; it might be more efficient for loaded systems, but it's certainly not simpler.

Looking up an ID in a database is much simpler that using JWTs (which involve crypto) and handling revocation.

Libraries might hide a lot of the complexity of JWTs behind abstractions, but that doesn't mean it doesn't exist.


Oh please... you are seriously going to argue it is less complex call a database or redis than it is to locally validate a token using proven crypto methods that have existed for decades?

If you are gonna play the “look under the layers of abstraction to count true complexity” game.... your assertion falls apart in a hurry. Calling a database or redis every http request is a hell of a lot more complex in terms of call stack, configuration, network dependencies and library support than JWT.

But it is a stupid way to frame your argument to begin with. Both ways abstract all their doings behind a library. Both probably have almost the same general configuration foot print. And JWT has the benefit that you can still treat it like a session token and validate on every call for parts of your site that need it. Most sites do not need to instantly revoke a token everywhere!

The number of detractors around here who don’t understand that last point is pretty sad...


While I think your response is overly harsh, I could have phrased my argument better. What I'm getting at is that the JWT spec has issues, JWTs are complex to get right, and they have (notorious) encryption footguns. Connecting to a database is a relatively simple affair.

For the record, I'm not a JWT "detractor" (if you look elsewhere in this thread you'll find a comment where I used it to good effect).


JWT helped me to achieve authentication and authorization without needing to make a trip to database on every API call. I use to store user role code in the token.

I kept the token expiry to an hour or two depending upon the usecase.

Added logic to client to refresh the token right before expiry of existing token. Otherwise, on token expiry, server returns 401 and client can attempt to refresh token or send user to login page.

If there is a need of instant revocation, then I can maintain a list of revoked tokens for the duration of token life (1-2 hours) in cache layer. After 1-2 hours token will be expired anyway.


Why do you need a db call for api request when you can just cache session data?


In memory caches have scalable concerns and require sticky load balancer. You'd need to decentralize the cache (eg create a db for the session information) to allow horizontal scaling of services so this approach is generally considered old-school.

JWT has its own slew of problems, most of them are temporal (eg invalidation of all sessions), usability (eg short expiry), or additional security vectors (many poor JWT implementations, accidentally authenticating invalid tokens w/o signing, careless storage of readable values)


Hmm never had an issue with in memory caching and scalability (except in the old days of memcache). One can send a unique and hard to guess pair of identifiers and drop the lb stickiness. anyway depends on the use case, but i feel like given the symptoms you describe there are a few other ways of doing it without jwt.


For sure - they're just considerations/complexities if you need to quadruple your service infrastructure for black friday or what not. I don't feel JWT is often appropriate - I wrote one of the early scala implementations so have seen the lifecycle of a security library.


JWT are effectively that, a signed token that contains the session information.

You could build a session cache, either in memory or out of process, but it would still be more work than the token approach


Typically, before stuff like JWT was popular, you would store a session in the db and the session id in a cookie. Then you would hit the db each request to check if the session was valid.


Well not really. Storing session data in the db can hit your performance pretty bad. Storing them in mem is a better option.


These are not mutually exclusive options. Redis is a database and runs in memory.


Actually no, it’s an “in-memory data structure store, used as a database, cache and message broker.”.


Yea, that's why people moved to auth headers.


I’ve found that writing starless services makes my life significantly easier. Also I may need to query several apis to render a single page. Each one needs to know who is calling it.


Frankly, I just went back to sessions.

Everyone thinks they're building the next Facebook, but most of us aren't gonna stress out even a single Redis instance.


Yea, I went back to sessions (with Redis) as well. So much simpler, since all of the major web frameworks/libraries have much better support for traditional sessions than JWT...

One place I still might use JWT is in service-to-service auth.


In that same mindset, why not just do a JWT in a cookie then?


Because then you have all the headaches of JWT, without any of the benefits of stateless auth tokens.


Which headaches would that be?


That you have to keep a white/blacklist if you want to revoke a token.


Blacklisting is only half the problem. Trying to emulate the same UX of regular sessions (staying logged-in etc) is the bigger pain point.


Because trusting the client with data is always a risky proposition.


Sessions are no different in that respect. You need to sign your JWT or session cookie.


I see no reason to sign a session cookie that was issued by and will be validated against some authentication service. Especially if the cookie is sourced from a cryptographically-secure pseudorandom sequence of bytes which is sufficiently long to avoid collisions with other such sequences (e.g. 256 bits of entropy should typically do per session cookie).

In my implementations of such schemes, I will typically feed the CSPRNG output with a time-based salt into SHA256 or SHA512 in order to generate the final token, which is simply stored on both ends (user session db row on server & cookie on client). At this point, the server can simply 1:1 the supplied client token with the row in the database to determine if the session is valid and which claims to return (or which validated final server response to return as the case may be).

Advantages with using a token made out of pure entropy soup are that it's consistent throughout the entire user's session, and confers absolutely zero information regarding the nature of that session. Disadvantages are honestly unclear. I feel like JWT and other stateless schemes started as a bandaid to re-couple the authentication domains of disparate business services, but by way of the various clients.


This property you use is known as an ephemeral session key and is a requirement for perfect forward secrecy. (Not being able to crack previous messages by compromising a key in the future)


You can store data in the server-side session the user is never intended to see. If you want to store that in a JWT, you're going to have to encrypt it, not just sign, and even that may clue them in that something's going on - if you perform some potentially spammy act on a site and suddenly the JWT gets updated with a big encrypted block, you probably know you tripped some flag.


You can. What does that have to do with traditional sessions?


Who cares what the current thinking is - define your problem and implement the solution that makes the most sense to you.

IMHO, the problems with JWT are overblown. Yes, people have made huge mistakes implementing JWTs, but the fault lies with the implementation not the standard.

Stateless authentication tokens are always going to problems with revocation. There is always going to be a limit on how much you can cram into them. These problems are not unique to JWTs.

JWT has the advantage of being a standard with good support across languages. Other schemes might be slightly better at handling larger amounts of session data. Take your pick.


Some of the mistakes people commonly make - insecure ciphers, for example - should've been addressed in the standard.


My sentiment: use sessions in cookies the way people have for years if what you're doing is managing sessions.

There isn't really such a thing as stateless auth tokens. For authentication you need revocation. For authorization, you can't stick that in a token that you send out because permissions can change. So you end up having state that you distribute everywhere anyway, so do it in the easiest way possible. Scaling fast lookups of a session key is much simpler problem.


One of the secrity experts here at HN always recommends against it for auth. Go to the search box on HN and type "author:tptacek jwt". Make sure you are searching within comments. You will see his multiple responses over the years.

I have mostly stuck to plain old server side sessions that are easy to revoke. If anyone is using Go, I highly recommend this library :

https://github.com/alexedwards/scs


> I have mostly stuck to plain old server side sessions that are easy to revoke.

But you have implicitly made a tradeoff. Your front-end infrastructure now has a very strong dependency on your auth server. Any minor hiccup in the auth system will blow up your downstream clients. Worse, in large sites you will be sending a ton of traffic to your auth server.

You've also introduced a source of latency to your page-load times as each request now needs to call the auth server to validate the token. Whatever the response time of the auth system now gets added to the front-end response time.

Going with tokens that can be validated on the front-end system can dramatically lighten load on your auth system and reduce your front-end response time.

The only major trade-off is you can't kick somebody out instantly. The bad-actor can do whatever until the token expires--a knob you control. And even then, for shit that matters, you can always have the front-end server validate the auth token on each request anyway.


I think there are two great options:

- https://paseto.io

- http://macaroons.io

I use simple http only cookies with the following setup:

    "cookie_name=cookie_value; Domain=sub.domain.com; Path=/; Secure; HttpOnly; SameSite=Strict; Max-Age=31557600"
This is valid for a year when you can decide if you want to renew it. Everything else stays on our end and we controll sessions, permissions, etc. Javascript (in theory) cannot access these cookies.


What makes macaroons great? I read the whole documentation and the macaroon.js docs. It seems to be under-documented, the library last updated 3 years ago. I couldn’t really grasp how the mechanism is safe re. clients adding “self-attenuations” that would amount to a DoS on the receiving service. Is anyone using this in production?


Credential attenuation in Macaroons is cryptographic; it's in how the tokens are constructed. I don't see the opportunity for a DoS (that didn't exist without attenuation already).

Macaroons are a really lovely, tight, purpose-built design that happens to capture a lot of things you want out of an API token, including some things that JWTs don't express naturally despite their kitchen-sink design.

JWT is more popular because there are libraries for it in every language, and people don't think of tokens as a cryptographic design (or nobody would be using JWT!), they think of them as a library ecosystem. JWT is definitely the stronger library ecosystem!

This is also why I probably wouldn't ever bother recommending PASETO. If you're sophisticated enough to evaluate token formats based on their intrinsic design, then you should implement Macaroons if possible (it's almost always possible). If you're not, then you're going to use JWT.


> nothing’s stops discharge macaroons from containing embedded third-party caveats ... to consider

My understanding is that the receiving app has to contact each third party to retrieve the “discharge macaroon”. What stops someone from adding a bunch of dummy caveats to cause *N requests on the server?


No, the discharge is indicated to the verifier cryptographically. The user of a 3p-caveated macaroon has to request that discharge from the 3p before sending it to the server.


I don’t know what else to say - maybe the macaroonjs documentation is wrong? Full quote:

> nothing stops discharge macaroons from containing embedded first- or third-party caveats for the verifier to consider during verification.

So the user requests discharge tokens on his own, but the discharge tokens have to be verified by _my_ server and might contains nested third-party caveats which I’ll have to verify.


If the discharge has another 3p caveat, then generally you'd have to get that discharged also before sending it to a server.

Either way, in each deployment a server supports a set of caveats that makes sense and rejects others.

Or that's the intention of the original idea (I'm an author of the paper). I can't speak for macaroonjs specifically.


Are you sure you're protected from CSRF from older browsers? There's still plenty of IE going around on old desktops in libraries and such.


Not OP, but wrt to enterprise apps, SameSite protects from CSRF even on IE since June 2018 update. https://caniuse.com/#feat=same-site-cookie-attribute


Based on the user base this is a negligible minority for us, that we do not want to care about. We can simply display an error message saying that you need to use a newer version.


Everyone seems to be posting the "stop using JWT" article from 2016. It has a lot of merit, but I think there is small logic hole in it. When the second part attempts to address the issues of token revocation, its only argument is "what if your blacklist is down?". But that is silly. Blacklist is part of your authentication process, if it is down you don't authenticate. Its like asking how would you serve your API if your API server is down. You won't, your server is down.

Otherwise, blacklisting tokens as a way of revocation process seems just fine to me. Set reasonable expiration to the key, allow users to refresh keys, then when you need to revoke a key, add it to blacklist together with the expiration timestamp and expire it out of the blacklist at the same time the key itself would expire. You can do all of this by magic by using Redis for your blacklist storage and setting expiration on your blacklisted keys. Yes, you would need to check your blacklist on every request, but a) blacklist is going to be smaller than a list of every issued token (like you would with normal sessions) b) you don't even have to store the whole token, just calculate some hash and store that, now you only need to read a few bytes on every request instead of reading the whole session.

The author does make a some good points though.


The argument is that the blacklist is as much a SPOF as a session server would be. The blacklist kills the statelessness.


Does that negate all the benefits though?

If a single component of your application maintains a little state, say the API Gateway, and all the downstream services simply get a clean JWT they can trust with all the right claims in it?

I'm thinking this is still nicer than having every downstream service also having to check auth claims against some datastore.


Yes, because then the API gateaway could instead just use traditional sessions and attach relevant data when forwarding the request downstream.


Exactly. Even if it's not a SPOF, it becomes yet another piece of architecture you need to worry about distributing and scaling (if you're at the point where JWTs are attractive to you due to their scalability).


It's also possible to do your token validation in something like Cloudflare Workers and store your blacklist in Workers KV. That would mean it lives all around the world closer to your users than your origin, making it unlikely it is adding meaningful latency to check it.

Disclaimer: I work at Cloudflare


But there’s a limit of 5000 requests per 10 minutes with cloudflare workers. This kind of usage will never go well with that limit.


Where did you see that? I've skimmed their limits page [0] and there doesn't seem to be any request limits for paid plans.

[0] - https://developers.cloudflare.com/workers/about/limits/


There is no such limit. We have individual customers doing hundreds of thousands of requests per second.

(Disclosure: I'm the lead engineer for Workers.)


JWTs and sessions are not in any way orthogonal in a system as a whole. We use JWTs primarily for scalable backends - if a session is setup or not in the front end is not a concern for the API backend services. In a microservice environment there could be many of these - that’s the whole point after all. And with JWTs being sent to these they can simply verify the integrity of the caller without asking a central authority. Coupled with OAuth2 and OIDC as protocols for delegation/federation and you have a system that scales extremely well. Of course, it might not be the first concern that needs to be addressed in a small startup but that doesn’t mean it doesn’t have a place.


In my experience, the idea that JWT is stateless is wrong. When your client log-out you will need to keep the token in a blacklist and check all requests against it to make sure it is not reused until it expires.

Since you need to keep a list of invalid tokens, it's not easier to just use standard token. If what's you like in JWT is the ability to store a payload that your backend gets back, you can keep that in your store alongside the token and read it at the same time you check the token is valid.

IMO, JWT only makes sense when an other entity is doing the authentication of your clients.


> When your client log-out you will need to keep the token in a blacklist and check all requests against it to make sure it is not reused until it expires.

Sounds like a great way to DOS a server by running out its disk or memory space. (unless there's some sort of rate limiting in place)


How does keeping a short token in redis or other would make the application easier to DOS than any other resource?


Because keeping a blacklist means keeping a list of all invalid tokens that haven't expired. If a user creates a crapload of sessions, you have to keep a running list of all the invalid tokens until they expire, which creates a few problems. You now have to have some mechanism to clear out tokens that have expired, which totally defeats one of the benefits of JWTs. And keeping a blacklist opens up the doors to someone slamming your /session endpoint, eventually running out your storage and/or memory because the blacklist will just keep growing. Sure, you can build in rate limiting, which you should have anyway. The reason I think this is still a problem is that a lot of amateurs are going to go straight for JWT and are too inexperienced or lazy to implement rate limiting.

But why add all this complexity? Just have a whitelist, and you avoid these problems.


We need FIDO2 or some other standardized security key protocol in our devices.

I recently got an Apple Watch and having to just tap on my wrist to authenticate is amazing.

Would love to just be able to give a website one way to contact me and have it ping my device to authenticate with faceid, touchid, Windows hello, or an smart watch.

Basically, a magic link on steroids.


JWT bearer tokens have worked well in my experience, though I haven't used PASETO. The main benefit is being able to use the same authentication mechanism for mobile and web apps.

The biggest drawback is the inability to revoke tokens without giving up statelessness. Keeping a KV blacklist isn't the end of the world, especially if expiration times for tokens are short. But at that point, the cost/benefit vs cookies+sessions gets blurry.

Some general JWT tips:

* Use a sufficiently long secret key if you're using HMAC based signing (https://auth0.com/blog/brute-forcing-hs256-is-possible-the-i...).

* Use bearer tokens to avoid CSRF attack surfaces.

* Avoid long (or non-existant) expiration times.


I was torn, but recently I found Inertia.js and love it. It uses server session for auth, and makes vue a bit like turbolinks, and I'm able to use vue without router/vuex, so for a fullstack but better at backend guy like myself it makes more sense. Look at their pingcrm for a good intro using laravel.


Just use stateful tokens because if you are dealing with sessions and you are doing it right, you WILL want to revoke them at the user's will or at some suspect activity in a session.

I've wrote how to revoke a JWT (https://dadario.com.br/revoking-json-web-tokens/ - caveat: requires a db lookup), but if you are going through the trouble of revoking a JWT, just use a stateful session instead. It's way easier.

You can use JWT to pass along user information to other components within your architecture, that's fine :)


I feel like JWT is a poor version of aead cookies like rails uses.


I'm not sure I understand the people claiming you should use server side session tokens instead. How am I supposed to make that work with Angular? I have to be able to send to the client the user privileges somehow.


When logging in, the backend will generate some state (e.g. user privileges) and store it somewhere, alongside a random session ID. It might be stored on disk, in a database or in a KV store like redis.

The backend returns this session ID in a cookie.

You store this session ID as a client-side cookie, which gets sent on every request to your backend.

When your backend receives a request, it will (typically using some kind of middleware) lookup the session ID in the backing store and deserialise the state.

Sliding expiration is typically used, so as long as you keep making requests, you'll remain logged in.

Revoking a single session ID is very easy, since you've just deleting a single row/key/line from your backing store.


It's pretty easy with authentication cookies and csrf tokens. I switched back to sessions too after messing around with jwts for a bit.


I would be grateful if anyone could point to a "how to build a PKI based (client and server certificate based) ecosystem"


Even if your tokens are stateful, they should be signed.


Where’s that guy from that Oauth company that always pushed JWT, I think he was with Okta

Its fine to be wrong its also hilarious


This isn't a technical answer, or me saying that there are not better options, however with JWT's being one of the corner stones of Oauth and OIDC, it would be hard to argue that there are not successful implementations.


The biggest thing to worry about is a hash length extension attack. https://blog.skullsecurity.org/2012/everything-you-need-to-k... This requires avoiding the use of a MAC hashing algorithm such as sha1 or even sha256 because it's possible to append data and generate a valid signature from a previous oracle. Alternatively, you could use SHA2 to generate a 512-bit key and then truncate the key to 256 or use an HMAC


We use encrypted JWT tokens. I'm not sure it was the best choice, but it was the simplest for our needs.

Basically the developer is responsible for generating & distributing auth tokens for each device that they want to use our service.

This service is designed to fit into games and AR apps, so we didn't want to force our own oauth flow on the end user. This way, the developer can generate a token on behalf of the end user, and distribute it how they see fit. (assuming one token per user, but it don;t overly matter, it's still tied to their account.)


I'm doing everything by hand. Like byte array with timestamp (4 bytes), IP-address (4 or 16 bytes), user id (4 bytes) and so on, then this array is signed and converted to string using base64. I looked into JWT and it was absurdly long. I don't like to waste bytes.


“I don’t like to waste bytes” isn’t exactly a great reason to write off a widely accepted and deployed spec for transmitting data in a trustworthy manner and instead write your own implementation, which most likely hasn’t considered all the attack vectors the JWT (and broader JOSE) spec has considered and accounted for.

I get the motivation — and I hesitate a little bit to say this — but it’s 2019, and a few “wasted” bytes on your auth token are less than inconsequential in a world where your front-end codebase mostly like “wastes” thousands (if not millions) of bytes.

When it comes to securely transmitting data, it most often pays to lean on the work and research of others.


I prefer to keep it simple. JWT does not look like a simple thing.


HTTP/2 HPACK compresses headers in such a way that the difference will be largely irrelevant.

Most connections nowadays are such that latency is overwhelmingly more important than that many bytes.

Also, IP address? You invalidate me every time I switch networks?


Absolutely, if hacker steals token, he'll likely using another IP address, so token is bound to IP address, it's a very important security measure. It's easy to issue new token anyway and address does not change often.


Signed how?


Using HMAC.


JOSE and COSE fill a void not filled by anything else on the market.

Sure, Branca may compare to just a JWT, but it doesn't compare to a JWS let alone JOSE.

If all you need is stateless auth tokens, use Branca or a good JWT library.

IMHO, the problem is the libraries, not the JOSE(JWS/JWE/JWK/JWT/JWA) standard. IMHO, after ~4 years there's still no good Javascript or Go library for JOSE, but potentially a few good JWT focused libraries.

https://blog.zamicol.com/2018/03/its-jose-not-jwt-pedantic-c...




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: