Hacker News new | past | comments | ask | show | jobs | submit login
Anatomy of a JWT (fusionauth.io)
113 points by mooreds on March 1, 2022 | hide | past | favorite | 38 comments



This is a great, easy to understand breakdown, thanks a bunch! I'm at the tail end of having to transition an existing, not well documented codebase from Identity Server to Keycloak and had zero experience with auth prior to starting. I found the jargon to be the toughest part, as well as learning the hard way all the little things that more experienced devs take for granted like the subtle differences in how the backend API is granted auth vs. how the front-end clients are granted auth.


Modern claims auth just sucks to learn. You're not alone.


https://jwt.io/ is also a great resource for analyzing JWTs interactively (with the usual caveats about pasting sensitive information into a random text box).



I respectfully disagree. (Full disclosure: I'm the author of the post and work at a company with software that produces a lot of JWTs.)

Like any tool, JWT can be misused and you can do insecure things with it. I actually quote @tptacek in a different article about JWTs: https://fusionauth.io/learn/expert-advice/tokens/building-a-...

However, JWTs have strengths too. They are:

   * flexible
   * widely supported
   * standardized
   * stateless
   * portable
   * relatively easy to understand
   * extendable
They also have libraries (often open source) to help you generate and consume them in almost every programming language, which is a huge help when it comes to using a tool securely.

Edit: fixed typo (relative -> relatively).


> stateless

This is a bug, not a feature, especially if you make your tokens long-lived. If your implementation is completely stateless, then there's no way to invalidate a token to allow a user to log out. If a JWT somehow gets stolen, the user has no way to terminate the stolen session.

At best, you could create a blacklist of tokens that have been invalidated, which would delete rows as time goes on and the tokens reach their natural expiration date to avoid the database growing forever. But at that point, it's no longer stateless.


> especially if you make your tokens long-lived

Agreed! You should never make tokens long lived. Seconds to minutes is what we typically see.

"But won't users have to log in whenever the token expires?" Nope, that's what the refresh grant (in the OAuth world) is for. I am not as familiar with other uses of JWT, but I suspect there is a similar process that relies on the issuer for them.

> But at that point, it's no longer stateless.

Agreed, it is an architectural choice. You can choose the benefits of statelessness (scale) at a cost of revocation difficulties. You can get revocation, but you have to re-introduce state.

Is there some token solution out that that I'm unaware of that gives you statelessness and revocation? Would love to learn more about it if so.


The refresh grant is itself a long-lived, difficult-to-revoke token. TANSTAAFL.


How are refresh tokens difficult to revoke?

They are presented to the AS to get a new access token, and the AS has pretty good insight into whether a user is logged in or signed out, right?


Just like a session cookie is long-lived? At least with a refresh token you can keep things like specific roles etc. out of it and treat it much like an opaque session cookie (perhaps just containing nothing but the user's ID, which can be looked up to get the real roles/permissions for the short-lived access tokens).

I think it's dubious to call refresh tokens "difficult-to-revoke" but call sessions easy to revoke. Both require some kind of state. The difference is that you are (hopefully) doing far more grants and checks on tokens than you are doing revokes - so why not optimize for that flow?


Session cookies are generally server-side stateful and trivially revoked, which is why we don't worry as much about their lifespan. JWTs are generally stateless --- that's one of their top-promoted features --- and are, in fact, difficult to revoke.


I still don't see how this makes it significantly more difficult to manage or revoke a JWT than a server-side session. For the session you remove it from the DB, and with a JWT you add it to a blacklist.

About the same amount of effort from my perspective, and revokes are probably extremely rare by comparison to issues or validations of tokens/sessions...


Session cookies are just a password for the server to lookup the information _on its side_. So when you revoke the server's portion of the session, it no longer exists even though the cookie is "valid".


Right, I understand that. JWTs can be revoked using a similar server-side mechanism - just that it's more of a blacklist instead of a whitelist.

And since you are likely adding more sessions than you are manually revoking them, why not optimize for that flow and use JWTs? Sure they may not be 100% stateless if you have to maintain the blacklist, but I'd say 99.9% stateless is still preferable over 0% stateless for many applications.


The main advantage of JWT, at least the advertisement historically, has been they are stateless and scalable, but that black list makes the system stateful again and you loose out on most(or all scalability) benefits. I think how much you loose depends on the specifics of the implementation, you definitely seem to loose any simplicity of design though from what I have read/worked with.


Yes, but also no. Typically you might need a valid jwt on every request, but a refresh token request on a different (hot) path only every 5 minutes per user. And revalidation might easily query a whitelist (sql/db - is user valid for login/refresh) or a blacklist (is refresh token invalid?).

The jwt being valid 10 minutes (accounting for clock drift) might be a problem - but probably sufficient for many applications?


JWTs can totally store state. I've found good use cases for it and I've never understood why people immediately get up in arms about it - possibly conflating the concepts of JWTs themselves and OAuth 2.0. It's just a damned signed json blob. Trying to act like it's anything more is missing the point.


The statelessness being discussed here is about server-side statelessness. It allows you to build a server that gets all the needed information about a transaction from the data being provided (url, body, headers) rather than having to look up authentication information based on a token that the request carries. This is valuable when you want to keep your servers very scalable without relying on a shared data storage mechanism that they must all keep up to date with authentication tokens and data.


> But at that point, it's no longer stateless.

Isn't that what you want ?

You seem to be agreeing that JWTs don't have state baked in, but can be tracked to the granularity you want, to adjust for your specific use case. Also I'd be a proponent of tracking all the valid tokens if you care about invalidation, it gives more flexibility and you can report to your user their connected apps/devices.

On the general point, I think it's easier to add state and context to a system than to remove it, so I kinda agree with the tradeoff of having no state by default, even if for most public facing applications low granularity tracking will be needed.


I think we're in agreement.

We need our session tokens (Whether a random session ID or a JWT) to be able to be invalidated, which requires server-side state, but proponents of JWTs advertise statelessness as being a feature.

Granted, with JWTs, you'd only need to store a blacklist of invalidated tokens, whereas with plain session IDs, you need a database storing the data for every active session, so JWTs can still be used to minimize state.


You could easily put a session ID in it (and check against a DB), or have a field store a short expiration time stamp (and reissue periodically) if those matter to you.

It just isn’t REQUIRED.


Putting a session ID in it and checking against a DB defeats the supposed "stateless" benefit of JWTs.

Being able to use a current JWT to acquire a refreshed JWT doesn't prevent attackers from using stolen JWTs indefinitely. A short time stamp just means they have to catch the JWT live and not just steal it from some storage.


Not at all?

A ‘stateless’ JWT with a unique session identifier allows you to check state WHEN YOU WANT, but not when you don’t. So you can use it statelessly to your hearts content, or statefully when you need.

And the short time stamp definitely doesn’t mean an attacker can use it indefinitely, it means they can only use it for a short time before they have to check in with the backend that generates tokens to get a new one - which that backend would know that ‘logout’ or whatever had happened, and not generate one for it.

It sets a maximum period of time where old tokens can be used in a stateless fashion without ‘checking in’, while also not requiring a full synchronous check of a database on every usage of every token.


I think most folks would run away screaming from a codebase that did and did not check session data. Hopefully any company that does this has a lot of money set aside for bug bounties.


Every sizable system I’ve ever seen does it to some extent. It’s super common for static content with no security implications to not bother with a live check, or a system with high load but no major security implications (read only content for instance) to not do synchronous checks.

Especially if we’re talking 5-10 second session timeouts, it’s rarely even a theoretical concern and dramatically reduces load.


Why not PASETO?


Widespread support is the biggest obstacle to PASETO adoption. Classic chicken and egg problem. I am also not aware of it being standardized (a brief google shows it is in progress, which is nice).

Other than that, it seems nice. A better JWT than JWT with some of the sharp edges removed.

I've actually filed an issue for our product to support PASETO and it's gotten some community support, but not an overwhelming amount.


Why use JWT? https://www.educative.io/edpresso/why-should-you-use-jwts

There are downsides for almost everything out there. Outright dismissing JWTs is not correct.


I do wonder what percentage of folks are actually in a position to choose? For a lot of people they’re going to be interacting with JWTs because they’re dealing with someone else’s auth, so the more they know about this structure the better.


That's a great point. As a dev, lots of times you're working with an auth provider, either from a social login like Google or from a centrally managed service.

In either case, you can suggest "let's use <X> token" where X is paseto or something else, but you can't dictate it (Google certainly won't listen and change formats :) ).


IMHO rule of the thumb is: If you can get away with simple random tokens, use that. If you can't (high-traffic distributed apps, microservices, serverless, etc.) JWT is probably the next easiest solution.


That's reasonable.

I see so many cases where someone asks "what should I use for auth" and immediately the answer given is JWT, where simple random tokens or session auth would be just fine. And simpler to implement, since they're supported out-of-the box in many frameworks.


This is a good point. Keeping it simple is always a good engineering choice.

I think one of the reasons JWTs come up so often is that if you are going to use OAuth2/OpenID Connect - ideally the Authorization Code grant, then tokens become an important component.

And many IdPs implement the OAuth2 access token as a JWT. So it may be that your IdP ends up making this choice for you. Then you have to learn how to deal with JWTs.


It's jarring to me how much tptacek's comments are usually very succinct and heavily factual, and this blog is ....long for no reason, very rambling (I don't need biblical references in technical discussions...), and way too emotional to be convincing.

My main takeaway from it that JWTs aren't magical, there's exploit vectors that need to be taken into account, and as any security related thing, you need to do your homework to have a decently secured application.

That's pretty far from "don't use JWT"


It's jarring to you because someone else reframed the post as "don't use JWT" (a thing I believe!), and that's not what it's about. See, here, the power of people to control the terms in which people read and think about subjects by creating their own titles to things!


Why not? The article does not really answer why not.

> This is a problem because the ability to read your user profile isn’t a good identity proof. You might grant that capability to applications for reasons having nothing to do with whether they can “log in with Twitter” to a dating app. People found a bunch of vulnerabilities.

> Enter OpenID Connect (OIDC). OIDC is the demon marriage of OAuth 2.0 and a cryptographic token standard called JWT. OIDC’s is unambiguous: it gives you an “Identity Token”, JWT-encoded, that tells you who’s logging in.

Ah, so it’s more of a case “don’t use jwt… incorrectly“.

I also recommended looking at UMA2 and resource servers. Keycloak has, what I’d call, my to go reference implementation.


Agreed, JWTs fill a niche, and there are also several footguns, and most people are better off avoiding them.


Really nice write-up.




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

Search: