macintosh.world | Log In | Register
Today | News | Books | Recipes | Notes | YouTube | QuickTake
Translate | Wiki | Browse | Maps | Reference | Reddit | About

Back to HN

Stop Using JWTs

by dzonga | 440 points | 257 comments | 2026-06-16 11:49:46 Central

Open Source Link | Read Source Here

Open on Hacker News

Comments

solatic
Necessary qualifier: for browser-based user
sessions.Plenty of good uses for JWTs for
service-to-service communication.edit: I read some of the
linked stuff, e.g.
https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-
ba... . Please, if JWTs are such a horrifically insecure
standard, go ahead and publish your means for hacking AWS
STS's AssumeRoleWithWebIdentity , or don't publish and
just exploit it by launching cryptominers in every Fortune
500 production AWS account. Let me know when you
inevitably succeed, because JWTs are so insecure, right?
/sarcasm

  > RagingCactus
> Necessary qualifier: for browser-based user
sessions.> Plenty of good uses for JWTs for
service-to-service communication.This is the sensible
conclusion right there. I agree JWTs are the wrong
tool for the use case of user sessions in the
browser.To give some more arguments:All the signature
and encryption stuff in JWTs is complex. While common
JWT libraries have now mostly got their stuff
together, this has not always been the case. There
were plenty of libraries accepting the "none"
algorithm [1] or allowing attackers to forge tokens by
using a public key as a shared secret [2]. This is the
direct result of the complexity criticized in the
linked blog post.JWTs also cannot do some stuff you
want for user sessions. You can't invalidate them
without keeping a revocation list somewhere. But if
you have to check an identifier for revocation on
every request you could just use an opaque session ID
and look that up on every request instead! Sure, you
can use short-lived tokens and refresh them all the
time, but why bother with that for a typical
application that has to keep some state anyway?All
that being said, I wholeheartedly agree that there are
use cases in distributed systems and
machine-to-machine communication where signed tokens
can be useful. Just please don't confuse the two
cases.[1]
https://nvd.nist.gov/vuln/detail/cve-2022-23540[2]
https://nvd.nist.gov/vuln/detail/CVE-2024-54150 (just
a random example from googling, I don't know what
library made this one infamous)

    > > nine_k
> if you have to check an identifier for
revocation on every request you could just use an
opaque session ID and look that up on every
request instead!One reason could be the size. A
revocation list only needs to keep session IDs of
recently logged-out sessions, for which the
token's TTL hasn't yet expired. It may be a much
smaller list than a list of every active
session.Also, a JWT (or a Macaroon, etc) can store
a large amount of details about the session in a
cryptographically secure, unforgeable way. This
rids you of the necessity to store all that in
your active session database, again cutting the
size.

      > > > agwa
As someone who operates a PostgreSQL database
containing 27 billion SSL certificates, each
1-2kb each, with a bunch of secondary indexes
that get inserted in random order, I find it
pretty incredible that people see the need to
optimize their session database. At what scale
does the size of the session database actually
matter?Those stateless tokens may be
"unforgeable", but they are replayable, and if
you're not mindful of that you can have
security vulnerabilities.

        > > > > btreecat
We have a single harbor instance at work,
backed by PG. The folks ran into some sort
of performance issue during their testing
(not documented) and their solution was to
use cloud native pg, run two postgres, and
add additional second layer of connection
pooling.We eventually hit a bug in a
combination of certain images, docker
desktop (not even supported by us), and
permissions. The error looked related to
the connections, so I suggested the pooler
be bypassed as a test. They ignored the
suggestion for two weeks until it was the
last thing to test.Sure enough bypassing
the pooler helped stabilize the connection
from harbor during image uploads.Now I
need to talk to them about properly
vertical scaling PG before we try and run
two with replication, in the same kube
cluster, in the same physical data center.

        > > > > mewpmewp2
I think one meaningful case is when you
have services in very different locations
and you would rather than having to make a
request to a session store in a single
location, replicate the data to each
location for better latency, so in this
case a revocation list.

        > > > > hparadiz
You should do some basic optimizations.
Fixed length table and indexes on the
unique string for fast lookups. I also
like to do a rolling delete for old
sessions after 30 days unless mobile
session that is logged in. Those get to
live forever.

          > > > > > agwa
Fair enough, but those optimizations
are basically free. People think
stateless tokens are free but they
really are not.

            > > > > > > hparadiz
The cost of the stateless token is
basically the CPU usage for
signing the message and checking
the signature with the public key
on the client. Example: Google
Compute Instance asks metadata
server for OIDC token (which is a
JWT). The metadata server respond
with the token that basically says
"here's the machine service
account, here's the machines ID,
this token is proof that I am
service account abc123 and it's
valid for 20 seconds". This is one
of the most common uses of JWTs in
enterprise. You don't store them.
They actually are free.Lots of web
devs get tricked into using them
as primary session tokens and it's
a huge anti pattern. I see it all
the time and people get aggressive
about it.

            > > > > > > agwa
The cost is the vigilance required
to use them safely. It's not just
compute/storage costs.

            > > > > > > hparadiz
I didn't downvote you. You're
absolutely right. Implementation
of anything is work.

            > > > > > > locknitpicker
> Fair enough, but those
optimizations are basically free.
People think stateless tokens are
free but they really are
not.Strawman.The only requirement
for a JWT is posting the JSON Web
Key set with the public keys used
to verify the JWTs signature.
That's the full cost of a
no-frills JWT implementation of
you exclude IAM.If you want to
have one-time JWTs you need to
maintain a revocation list. This
is literally a set of IDs. If you
go nuts and use GUIs for JWT IDs
that means each entry takes as
much space as 4 ints, and all you
need is a set membership check on
said integer. Even at FANG scale
you can handle that scale in a
memory cache service such as
ValKey running on a COTS
desktop.Now show us your
alternative.

            > > > > > > hatefulheart
Yes we have heard this before,
React is only 30kb! But that
misses the enormous amount of
infra you need to even just do a
basic fetch. (Read the post by the
React Query author on whether you
need React Query or not)Likewise
with JWTs for sessions you need to
handle cache invalidation,
revocation lists, key rotation,
the list of difficult comp sci
problems really does go on!The
same issue as always plaguing the
frontend world. Up front
"simplicity", enormous actual
complexity

            > > > > > > locknitpicker
> Yes we have heard this before,
React is only 30kb!Not quite. You
might be surprised to know, but
the whole JOSE standard, and JWT
in particular, specify a very
limited set of fields. Whenever
anyone starts requiring more than
that, the responsibilities start
to be offloaded to the likes of
OpenID Connect.

            > > > > > > dns_snek
> If you want to have one-time
JWTs you need to maintain a
revocation list.No, you always
need a revocation list if you want
to handle user sessions in a
secure manner. What claims do your
tokens contain? If it's anything
other than some stable
identifiers, like user name,
email, permissions, etc. then you
now have a cache invalidation
problem.But if all your token
carries is an identifier which you
need to look up, how's this any
better than a signed cookie
containing the session ID? All
you've done is add complexity.

        > > > > lmm
What do you do about availability? AFAIK
the choice is to pick one of 3 or 4 hacky
difficult-to-administer clustering
solutions, or have that single PostgreSQL
database be a SPOF for your whole system.

        > > > > nish__
27 billion? Do you work at AWS?
        > > > > stickfigure
The issue isn't size, it's load.
      > > > saganus
I am still waiting for Macaroons to be used
widely. I think they are a fantastic
invention.It seems they were not of very much
use in the past, but with the
agentic-everything now, I see this as a great
way of delegating permissions to subagents,
third-party agents, etc.Working on something
along these lines but unfortunately I cannot
dedicate as much time as I'd like.Still, if
anyone is reading, give Macaroons a try!

        > > > > tptacek
We have what I believe to be one of the
world's largest deployments of Macaroons.
They're a mixed bag, though I think
they're a lot more interesting in a world
where agents do most of the fiddly
work.https://fly.io/blog/operationalizing-
macaroons/

          > > > > > saganus
I am very aware of your work!It's the
only prod usage of Macaroons I know
of, I think.Third-party discharge
seems like a great way to have
human-in-the-loop gating, among other
interesting things.Would be great
reading your thoughts if you ever
write about the agentic use case,
having all the fly.io experience

        > > > > malfist
I like the raspberry ones. Or lemon is
also good

        > > > > jiveturkey
JWTs can do that (delegate) and such
capability is already well defined.

          > > > > > saganus
Maybe I stated it wrong. Macaroons
have the ability to attenuate the
restrictions _without_ contacting the
auth server, which makes it IMO fit
for restricting and attenuating as
much as you want, without much cost.If
I need a roundtrip to the auth server
to attenuate, I am not necessarily
going to do it as often.

          > > > > > tptacek
Most token formats delegate. Macaroons
support attenuation, confinement, and
embedded third-party claims, none of
which are JWT capabilities.

    > > jappgar
Most requests are reads and letting someone use an
invalidated session for reads for 30 seconds on a
shortlived token isn't the end of the world,
especially considering that the exact invalidation
timing and its propagation is already somewhat
arbitrary.For rarer privileged actions you can
check a token revocation list.

    > > unscaled
I think both you and GP are somewhat
misrepresenting the OP is saying. OP's argument is
three-fold:1. JWTs are not a good fit for a
session token (although there are several RFCs
that are trying to shoe-horn JWTs into this use).>
TLDR: JWTs should not be used for keeping your
user logged in. They are not designed for this
purpose, they are not secure, and there is a much
better tool which is designed for it: regular
cookie sessions.2. JWTs have other "valid" use
cases that only need a very short-lived token
(e.g. a transit token or a request signature) and
don't need to care about user authentication,
revocation, XSS etc.3. But JWT should not be used
even for the "valid" use cases, since you have
better (read: less outrageously insecure)
alternatives nowadays.> Also note that "valid"
usecases for JWTs at the end of the video can also
be easily handled by other, better, and more
secure tools. Specifically, PASETOYou've noted
these issues yourself. There are many common
vulnerabilities with JWT: alg=none, algorithm
confusion and weak key brute-forcing, mandating
weaker algorithms like RSA and ECDSA while making
the best, fastest and easiest to implement
algorithms like EdDSA "optional".There are also
other design deficiencies that JWT makes by trying
to be a generic cryptographic envelope format
rather than a token format: e.g. expiration can be
omitted and this feature that caused some
libraries to not verify expiration by default or
have a different (and confusing) set of token
parsing methods that do not enforce the expiry.
PASETO is a better design that is secure against
all of these issues. Sure, there are a few minor
qualms I can find with PASETO (e.g. no mandatory
key ID and no support for non-JSON payloads), but
it's unlikely to face the same avalanche of CVEs
we got with JWT libraries.

    > > robertlagrant
> While common JWT libraries have now mostly got
their stuff together, this has not always been the
case. There were plenty of libraries accepting the
"none" algorithm [1] or allowing attackers to
forge tokens by using a public key as a shared
secret [2]. This is the direct result of the
complexity criticized in the linked blog post.I'm
a bit surprised at this. These are extremely
simple to solve - the first time I ever did a
JWT-reading implementation I specified the right
defaults, which are very simple, even for a
mid-level backend person I would say, and they
haven't needed changing in 8 years or whatever
it's been. It really isn't very complex.

      > > > agwa
You would think so, but even an authentication
company screwed it
up:https://cybercx.co.nz/blog/json-web-token-v
alidation-bypass-...

        > > > > userbinator
Clearly trying to be too general. I wrote
a tiny JWT validator before that only
allows a very small subset of algorithms
because I wasn't expecting the JWTs it
would handle to have anything else, and
obviously not "none".

      > > > jongjong
The people who are upset about JWTs probably
got burned by trying to use them in a weird
way. Some people try to store sensitive data
inside JWTs... WTF, the idea would never have
entered my mind! Sensitive data should stay on
your server. Encrypted or not! JWTs are
supposed to be signed, not encrypted! You
shouldn't even think to put sensitive info in
there. Also, WTF is wrong with people who
accepted algorithm "none." Most of these
people who tried to tweak the defaults had no
idea what they were doing in the first place.

        > > > > RagingCactus
> Also, WTF is wrong with people who
accepted algorithm "none."They dared to
use the default validation function of
their JWT library. They did not choose to
accept "none".And the library authors
implemented it because it's in the spec.
It doesn't excuse that the default was to
accept "none", but it is an explanation
and in my opinion a valid critique of the
standard.

          > > > > > robertlagrant
You could have a SQL library that
defaults to inserting "OR 1=1" to
every update query, which the SQL
standard allows. I would blame the
library.

            > > > > > > inigyou
The SQL library is meant to run
the query you give it.

          > > > > > jongjong
Yeah well it's definitely a footgun
built into the spec but ultimately an
library implementation mistake.

    > > 0x696C6961
The design I've landed over the years is to use
both. The cookie is a session token and that's
where you handle refresh tokens. Then there's an
endpoint where you can mint a short-lived
tenant-sepecific JWT. This holds the scopes &
tenant id. The session token only lets you access
the web assets & mint JWT tokens.

    > > chuckadams
A revocation list defeats the purpose of JWTs. If
you find yourself needing one, JWTs were probably
the wrong choice to begin with.

      > > > jongjong
Not really. Being able to verify a user's
signed identity immediately without having to
do any database or memory store lookup first
is valuable in itself.With session IDs, anyone
can spam/DDoS your system much more easily;
they don't even need to be authenticated to
waste your computing resources as they can
send plausible-looking session IDs and your
system will waste a ton of resources querying
your session store or database to figure out
that the session doesn't exist... It adds a
ton of latency and wastes CPU cycles across
multiple systems. Also stateful systems like
Redis are harder and more expensive to scale
than stateless systems like application
servers. Not to mention that they may be
depended upon by other parts of the
application so hitting those too hard can be
more disruptive. And that's kind of best-case.
Some people use a database to store their
session data...At least with a revocation list
with JWT. If the JWT says that the user is
user1234, then you know that this is a real,
previously logged-in user, they have an
account at stake, you can afford to spend a
bit of time/resources to check them against
your revocation list... And if they are on
that list, you can ban their ass and they're
done! They'd have to create a new account of
they try to spam you again. They can't spam
without a valid JWT and they can only get that
by authenticating with your server first.Sure
with JWT, some computation is spent on
verifying the signature but it's very cheap
using the default algorithm and only touches
one system... And you only need to call
another system once you know that the user is
valid.JWT is much more robust for DDoS
prevention because of this. But yeah, you
don't necessarily need a revocation list. You
can use short JWT expiries with frequent token
refreshes. Revocation list is good if you need
immediate ban.

        > > > > hasley
Can't a system be DDoS'ed with wrongly
signed JWTs as well?Is signature checking
(much) cheaper than finding an opaque
session ID in a database?

          > > > > > jongjong
Yes but it only impacts your stateless
app servers which are easier to scale.
Your backend services/stores are
protected and not affected by the
attack.

    > > locknitpicker
> All the signature and encryption stuff in JWTs
is complex. While common JWT libraries have now
mostly got their stuff together, this has not
always been the case.This is a red herring.
Applied cryptography was never considered an easy
subject in software engineering circles. Neither
was algorithms and data structures. Yet, it's
still a basic tool, and developers are still
expected to understand things such as why some
maps allow reads in constant time while others
require log time.Some libraries being buggy never
was an argument against using libraries. And do
you expect your single-purpose code not to be?I
think we are seeing in this thread a knee-jerk
reaction against perceived complexity. Yet, if you
sit down and compare JWTs with alternatives and
list all features along with pros and cons, you'd
be hard-pressed to even try to put together a case
for JWTs being bad.

      > > > RagingCactus
> Some libraries being buggy never was an
argument against using libraries. And do you
expect your single-purpose code not to be?Of
course you should use battle-tested and
well-maintained libraries for the really hard
stuff such as cryptography primitives.
However, that is not the point I was trying to
make.My points here are:1) If you can get away
with not using cryptography for something, you
probably should. Your web framework already
supports session cookies. Even if it doesn't,
it's very hard to mess up opaque tokens from
SecureRandom or /dev/urandom and a
corresponding database lookup.2) If you
actually need the things JWTs can do, the
standard is still needlessly complex and easy
to mess up in ways that are not inherent to
the problems JWTs are trying to solve. I'm not
saying this means you should roll your own
solution (again, I agree that there is value
in well-tested libraries), I'm further
strengthening point 1) with this. Don't use
JWTs if you don't need to

    > > LgWoodenBadger
Come on, it's not like the two are even within the
same magnitude or three"But if you have to check
an identifier for revocation on every request you
could just use an opaque session ID and look that
up on every request instead!"

    > > hparadiz
If you don't understand conceptually how to verify
a signature with a public key the very first thing
you should do is get that working and then work
from there. It's completely unacceptable to ship
without this.

    > > figassis
Stateless JWT revocation:
https://blog.nellcorp.com/new-aproach-to-jwt-revoc
ation/

      > > > dchest
WTF:> Each user has a secret: Stored securely
in the database.> Stateless Validation: The
core validation remains stateless. We only
need to consult the database for the user's
secret, which we'd likely do anyway for
authorization checks.Is "stateless" the same
as "serverless" now? Is author's brain
stateless?

        > > > > figassis
A JWT is usually signed, with a secret you
keep in your app. The statelessness of JWT
is that it contains all the information
you need to verify it. You do not need to
ask a db if the token is there and
valid.Storing a user's secret, the same
way you store your applications secret
does not make it more or less stateless.In
since you now have 2 layers of protection,
you don't actually need to verify agains a
user's secret immediately, you simply need
to check that the token is valid using the
app secret. The subset of valid tokens
that you need to check is much smaller
than the universe of all the unexpired
tokens your application has issued.If you
have a security incident and need to
revoke tokens for only a subset of your
users, now you don't need to rotate your
app secret and invalidate every single
token and break every single session. You
can simply log those users out.Is author's
brain stateless -- my bad, I thought this
was not reddit

          > > > > > dualbus
> [...] you don't actually need to
verify agains a user's secret
immediately, you simply need to check
that the token is valid using the app
secret. The subset of valid tokens
that you need to check is much smaller
than the universe of all the unexpired
tokens your application has
issued.What you are describing here is
different than what is described in
the blog post that you linked
to.Please look at the definition of
the function 'validateToken'. In
particular, notice how 'getUser'
function (which the author notes
issues a DB query) is called for every
JWT with a valid signature!EDIT: I
failed to realize that you are the
author of the blog post. Still my
point stands, in that your description
doesn't match what the code does.

            > > > > > > figassis
Maybe I should add a comment
there, but this is a compressed
example. Everything from getUser
onwards does not need to be in the
validateToken, it can be done
downstream, closer to where you
access user data. It does not need
to be a separate db call, You pull
a user and want to perform an
action, so data is in memory, use
the secret from there.Or if you
are doing inter service
communication, you can use your
app secret to validate that the
token can actually cross your
infra boundary (no user query
here), and each internal service
can then validate it in their
scope, or if a passthrough (like a
proxy), just forward it like an
envelope.What this does is give
you 2 secure layers, therefore
saving you from a lot of the
compute (drop expired and globally
invalid tokens at the boundary),
kill db round trips meant only for
token validation (attach to an
existing user query you already
do) and kill revocation list
management.

          > > > > > what
> Common workarounds like maintaining
a blacklist of revoked tokens
introduce statefulness, negating the
benefits of JWTs.> Validation: On each
request, we validate the JWT's
signature using the application secret
and then validate the sjti using the
user's secret.Having to lookup the
user secret from the db is no
different than consulting a list of
revoked tokens. You claim consulting a
list of revoked tokens to be stateful.
How is looking up the user secret
different?

            > > > > > > figassis
"which we'd likely do anyway for
authorization checks". Assuming
you are using a session token,
authentication != authorization.
You will eventually hit your
authorization logic to check if
the user can perform action X. It
may just be through claims in the
token, but I don't think most are
doing that. So you will eventually
pull user data and you can to this
check there if you are passing the
token through context.I have been
using this for a while, and I
haven't managed a revocation list,
I haven't done user queries at the
boundary and users are able to
logout and instantly invalidate
their JWT. I honestly haven't seen
this elsewhere without overhead.

          > > > > > dchest
Being able to quickly reject invalid
sessions identifiers is a useful
property in some cases and is normally
done by authenticating stateful
session tokens with a MAC using a
global app key. This can be used for
DoS protection if the cost of a
database lookup is more than the cost
of MAC, and the complexity is
justified. It ensures that the random
numbers a user is trying to present as
their session token are the numbers
generated by the server if the key is
not leaked.Because you're trying to
bolt things on top of JWT, you're
creating a worse version of that
stateful authentication pattern:1. You
lost the statelessness of JWT by
making database queries. Your claim
that "you don't need to verify against
user's secret immediately" is false,
as you need to do that in all cases
immediately after verifying the JWT
signature to get the benefits of your
system (token invalidation). Sure, you
reject completely invalid tokens
early, but you still need the
statefulness to authenticate users
properly (if your goal is to be able
to invalidate tokens).2. In your
version, getting a read-only access to
the user database (leaking per-user
secrets) completely destroys token
invalidation, and all your
authentication now depends on one key.
If, in addition to that, the JWT
signing key leaks, user authentication
is completely destroyed and can be
bypassed by the attacker, who now can
sign in as any user. (A common way to
leak all this is by failing to
properly secure backups).Compared to a
stateful session system with
split-tokens, where the database
stores tokenId => verifier, where
verifier is Hash(randomToken), and
user's token is id||randomToken,
read-only access to the database
doesn't let the attacker authenticate
as any user. If the tokens that users
presents are in the form of
id||randomToken||HMAC(serverKey,
id||randomToken) for early rejection
as above, leaking serverKey still
won't allow the attacker to
authenticate as any user. The attacker
needs write access.> Is author's brain
stateless -- my bad, I thought this
was not redditI didn't realize that
you were the author, I thought you
were a reader who was misled by this
blog post. Even better: you can go and
edit it, removing "stateless"
everywhere! It's fun to invent various
protocols, but when someone points out
the errors, surely you'd want to fix
them -- no shame in making mistakes if
you correct them.Usually, when I think
of a protocol, after writing down
"Benefits" (as in your blog post), I
write "Drawbacks" and then try to come
up with downsides and compare it with
existing protocols. I'd suggest you do
the same.PS. Find yourself in this
picture:
http://cryto.net/%7Ejoepie91/blog/2016
/06/19/stop-using-jwt-...

            > > > > > > figassis
1. You're probably correct that
some statelessness is lost or I'm
creating ambiguity. My concept of
it is that you don't need to
update the token and you don't
need to maintain state elsewhere
(ie. a list). Rotating the secret
simply means you're forgetting how
to validate it, you aren't
tracking the state of it in your
infra with a revocation list. If
we go by the script definition,
storing an app level secret, so
you can validate your issued
tokens is also not stateless.But
if we go with that, and downgrade
it to somewhat stateless, I think
it maintains it's value
proposition well, since I have not
seen many JWT applications be
fully stateless, especially wrt
authorization. So I took advantage
of that and bolted this so you can
use in during your existing authz
flow. So pushing to later does not
mean let's pull user data later
(that does not change the cost). I
mean do it when you have the data
so you don't have to pull it just
for this.2. Secrets are encrypted
at rest, you decrypt them in
memory. Of course, if your app
encryption key leaks someone can
decrypt all those secrets, but
this is an attack surface that
already exists. Not trying to
address that. Also, each user has
their own secret, so you have one
app level signing key, and N user
signing keys, you need to leak all
those and then get the app
encryption key to decrypt all of
them."Compared to a stateful
session system with split-tokens"
- sure, let me just tell every
company that is using JWTs to
migrate immediately to stateful
session tokens. One sec...done,
tomorrow will be a better day for
all of us :-)In all seriousness, I
posted this a while ago asking for
feedback, HN seemed more
interested in AI than actual
meaningful conversations. I
appreciate the feedback and will
update the description and make
the examples more clear. The
intention there was to compress as
much as possible and letting
readers implement their use of it
where it matched their existing
setups.Regarding your link, I took
a quick look (saw it a few years
ago, forgot about it), but seems I
inadvertently built option 6
"Rotate a user scoped signing key
lazily, during authz, on pre
validated tokens". This is
fundamentally different has a new
security and usability posture.
And it's still not a session token
as it keeps all JWT properties
that people are using it for (I
make no judgement of whether it's
the best solution for them, only
that they're using it).Thanks for
the feedback, there is room to
improve there.

          > > > > > stavros
If I need a database query to validate
the token, it's not stateless.

      > > > RagingCactus
> First, we need to add a token_secret column
to our users table:> ALTER TABLE users ADD
COLUMN token_secret;So it's "stateless" but we
have to query the users database on every
request? How is that more stateless than
SELECT * FROM session WHERE id =
cookie?Ignoring that and taking the mechanism
as given: Why the obsession with cryptography,
in this case HMAC? I don't see any reason why
another signature is needed here when I
believe the same outcome could be accomplished
with a token_epoch field in both the signed
JWT and the users table. Just increment the
epoch to revome old tokens. Or even better,
drop the epoch field and have an
iat_not_before field per user. The field in
the JWT is signed, the whole point is that you
can trust it.Do let me know if I miss anything
here please. Assuming I haven't: it's always
puzzling to me to see people being so eager to
sprinkle more cryptography on anything that is
supposed to be secure. For me, I've become
more afraid of cryptography the more I learned
about it. Cryptography is hard. It's not a
magic ingredient for security. At best, it's
dangerous black magic -- very potent, but
pronounce a single syllable of your magic
spell wrong and it _will_ blow up in your
face.

        > > > > figassis
You don't actually have to do a db trip to
get a user secret and revoke a token. A
token comes in, and you can store the
secret in the same place you store your
application secret. Because you do need to
store it, cache it, whatever. The point
here is you no longer need to keep a
revocation database of every token you
issued that is still unexpired. Just
rotate the signing secret and every token
issued until then will be revoked. Goes
from maintaining millions of tokens to
maintaining a smaller cache of user
secrets that are probably rarely
updated.Why not an epoch? because this
gives control to the user. They can now
logout regardless of token ttl. The point
is not obsessing over crypto, JWTs are a
cryptographic solution, it's what makes
them stateless and I have nothing agains
cookies or any other session token. I use
them interchangeably.My pain point was
that whenever I needed to use a JWT or
whenever I worked a company that used
JWTs, their main frustration was "oh but
then we can't revoke them easily without
maintaining a revocation list". Well now
they don't have to.Telling them just
migrate to "this or that technology" is
not how this works.

          > > > > > what
What? So instead of storing a
revocation list, you store a per user
secret that you need to consult. What
is the difference? How is that
stateless? How does it avoid a "rb
round trip" (where are you storing the
user secret)?

      > > > catlifeonmars
Wouldn't it be simpler to use a session token?
This complex machinery does nothing but look
fancy.The application secret is redundant if
the per-user secret is used.Also I'm inferring
from the article that the author is using
symmetric keys (HS256) for their JWTs. In what
world can you securely distribute symmetric
keys but can't use an opaque session token?

      > > > throwaway7783
"We only need to consult the database for the
user's secret..." , which kinda defeats the
purpose.

  > kyrra
JWT used to be bad due to libraries with poor
defaults. Downgrade attacks were fairly common a
number of years ago.Since most of the common libraries
across all languages have gotten more sane defaults,
it actually is pretty secure nowadays.

    > > tptacek
If we stipulate that, we're still left wondering
what the utility is of a standard that creates
affordances for the insecure defaults, as opposed
to just designing it right from the beginning.

      > > > jeswin
> utility is of a standard that creates
affordances for the insecure defaultsYou could
make the same argument about Cookies.> as
opposed to just designing it right from the
beginningAnd generally, it's quite difficult
to design it right from the beginning because
one would often start with the wrong
assumptions. Most standards evolve, and it
should be acceptable.

        > > > > tptacek
No, that doesn't square up. It's like
arguing "you could say the same thing
about TCP, because it allows you to build
JWTs, which are a bad protocol".

          > > > > > jeswin
Not the same. HttpOnly/Secure cookies
were added much later and was not the
default. They should have been
inaccessible to JS by default from the
beginning, and this policy has been a
source of countless attacks.

            > > > > > > unscaled
If memory serves me right, cookies
were designed by Netscape in 1994
before JavaScript was even a
thing. They were released in an
early beta of Netscape (0.9
something), while Javascript was
only added in Netscape 2.0. SSL
2.0 was only added in Netscape
1.0. So the HttpOnly/Secure
attributes were not relevant to
the first cookie design, which
wasn't even a standard.When the
first cookie standard (RFC 2109)
was released, the Secure attribute
was added. You could argue they
missed HttpOnly, but JavaScript
itself was highly non-standard and
underspecified mess during this
period (and for a while later
too). Almost nobody was thinking
about XSS as far as I can tell,
and that term was probably only
coined at least 2 or 3 years later
(by Microsoft researchers[1]). At
1997, the people who even
considered XSS, probably only saw
this as an HTML injection issue
that can be fixed at the injection
site and doesn't require any
special protections against
JavaScript code.If you really want
to point a finger at issues in the
early cookie design, then you
could talk about domain matches.
Not making the port part of the
matched domain and not allowing an
explicit way to trigger an exact
domain match with a specified
domain was a mistake. And the
confusing leading dot rules
(probably for optimizing a
substring match without parsing
the domain components) was also a
mistake.But RFC 2109 was replaced
with RFC 6265 and now RFC 6265bis
(which is not released yet, but is
mostly implemented by the big
browsers). These RFCs fix most of
the big issues we had with
cookies, and do not shy away from
breaking existing behavior:
setting SameSite=Lax as the
default and restricting
SameSite=None to secure contexts
broke A LOT of sites for improve
security. The changes made in RFC
6265 to forbid multiple cookies in
one header also broke many
sites.An equivalent approach in
the JWT spec would be a new RFC
that does the following:1. Forbids
implementing Unsecured tokens and
removes alg="none".2. Removes RSA
and ECDSA (or at least deprecate
them) and make Ed25519 the
"Recommended+" signature
algorithm. If we allow RSA and
ECDSA for compatibility, they must
be explicitly enabled with feature
flags or some other marker that
signals their insecurity and
security advisories on their
potential vulnerabilities must be
attached.3. Removes the entirity
of JWE as it is currently
implemented (to be replaced with
encryption else that is NOT
orthogonal to authentication).3.
Requires that HMAC secrets are
specified as binary base64url
data. They SHOULD be generated by
a CSPRNG or a derived using a safe
derivation method (such as
HKDF-SHA256 with safe key
material) and MUST be at least as
long as the "security size" for
the algorithm (i.e. 32 bytes for
HS256, 48 bytes for HS384 and 64
bytes for HS512).4. Makes the
"exp" claim mandatory to set, and
mandatory to verify.5. Add a
section with strict implementation
guidelines for libraries, e.g.
`parse()` functions that skip
verifying the token should have a
clear name like
`inspect_without_verification()`
or
`dangerously_parse_without_verifyi
ng()` and `verify()` function
should always receive a key that
is strictly typed to a specific
algorithm.6. Remove or restrict
the usage of fields that allow the
JWT sender to dictate the keys
used for verifying it, like "jku"
or "x5c". For instance, the
standard can mandate that when
implementing these fields, the
verifier MUST NOT accept any JWKS
URLs that do not match one of the
explicitly allowed patterns or
X.509 certificates that are not
signed by an explicitly trusted
CA.[1]
https://www.youtube.com/watch?v=mK
AWpFdVcPY

      > > > ForHackernews
Spec writers and library authors are human?
Who knew

        > > > > unscaled
PASETO and TLS 1.3 were also written by
humans. TLS libraries (which are several
orders of magnitude more complicated than
JWT libraries) are also written by
humans.If you passionately care about
security and misuse-resistance you CAN
write a spec that will lead to fewer
implementation issues.

          > > > > > ForHackernews
You must be young if you're pointing
to TLS libraries as an example of
doing it right and not getting into
trouble with insecure implementations
and downgrade attacks.
https://wiki.freebsd.org/LibreSSL

        > > > > tptacek
I don't understand what this is meant to
communicate. The standard is either good
or it isn't. "Good effort" is not an
engineering assessment.

          > > > > > ForHackernews
Your objection is that they should be
"designing it right from the
beginning" but that applies to all
realms of endeavour. The reason they
didn't is human frailty.If everyone
simply designed everything right from
the beginning we would live in
nirvana.

            > > > > > > andai
I read an article about business
which had this classification,
"Would be weird if it worked",
"Might work", and "Would be weird
if it didn't work" and argued that
you want to be in the last
category.In engineering we aspire
to a slightly stronger standard:
"I made it physically impossible
to fuck this up."

            > > > > > > ForHackernews
And yet
https://en.wikipedia.org/wiki/Hyat
t_Regency_walkway_collapse...

            > > > > > > tptacek
You've completely missed my point.
I don't even accept the premise of
the JWT standard. But the eventual
migration to safer default
settings, in a format that
continues to expend implementation
effort to support settings nobody
should use, is in fact a practical
engineering problem with the
standard.

            > > > > > > ForHackernews
And they've published updates[0]
and libraries have hardened their
defaults and removed support for
insecure values (e.g. alg='none').
I'm not sure what more you
want?I'd rather use a refined,
battle-tested standard with lots
of eyes on it than some new
untested contender produced by a
handful of upstarts ("look, we
just designed it right from the
beginning! This time it's
perfect!") PASETO reeks of
second-system syndrome.[0]
https://www.rfc-editor.org/info/rf
c8725/

            > > > > > > tptacek
I don't recommend PASETO either.
            > > > > > > doc_ick
What do you recommend then? What
technology has been designed,
completed, then used for years
without any updates or problems?

            > > > > > > kasey_junk
Bearer tokens are a dead end? You
have to validate them anyway so
traditional auth is the fallback.

            > > > > > > tptacek
https://fly.io/blog/api-tokens-a-t
edious-survey/tl;dr: most of the
time you should use opaque random
strings.

            > > > > > > ForHackernews
API tokens are a very small narrow
part of the authorization
universe. Having a shared secret
relies on a trust relationship
between the resource server and
the identity provider that does
not exist between, say, my SaaS
backend and Google or Meta's login
system.

            > > > > > > unscaled
The OP was talking about sessions
(which include session cookies and
API tokens). I'd argue these use
cases are far more common for the
average programmer than tokens and
signatures that are used for
federation, but I'll bite the
bullet here:JWT is a serviceable
solution for service trust and
federation. This use case often
just requires a very-short-term
token, so lack of revocation
support is not an issue. Replay
attacks are still an issue, but
they can also be prevented with
single-use nonces that are
included in the token claims.The
OP's take (and my take as well) is
that JWT is rarely the BEST
solution for this use case. You
kinda have to use it if you need
to implement a standard that
mandates JWT such as OpenID
Connect. But OpenID Connect is a
great example for a place where
JWT was used, but was never really
necessary. If you do use the
authorization code flow securely
(on the server side, with a strong
client secret and proper CSRF
protection) you don't need the ID
token. In fact, you don't need to
use any cryptography at all! Just
like random session IDs, you've
got a stateful solution that works
reliably without any
cryptography.If you cannot do a
series of authenticated network
requests between HTTPS endpoints
to verify trust, then a signed
payload could be useful, but
you've got better standards than
JWS/JWT for that. That's all.

            > > > > > > ForHackernews
> If you do use the authorization
code flow securely (on the server
side, with a strong client secret
and proper CSRF protection)This
restriction precludes all desktop
clients, mobile clients, and
webapp clients -- any place where
you can't trust the client code to
protect a secret.I don't exactly
disagree with you: Security
becomes much easier once you rule
out handling all the hard edge
cases.

    > > unscaled
JWT libraries had poor defaults because the spec
was poorly designed.Of course JWT can be
implemented securely. Even XMLDSig can be
implemented securely. But if the spec is not
designed with security and misuse-resistance as a
tier 1 priority, you will get more issues. The
fact that we didn't see the same sheer volume of
issues with PASETO or macaroon libraries
(admittedly, the later are far less numerous). I
can find only one CVE for a PASETO library from
2020, and this is an issue that has nothing to do
with the algorithm itself (JPaseto < 0.3.0
switched the order of two arguments in their hash
function call, generating weaker hashes).The
reason PASETO won't have the same issues as JWT is
the design (especially with v3/v4). There is no
alg=none, symmetric keys are fixed size (so no
weak keys can be used) and algorithm confusion is
prevented by an explicit implementation guide[1]
that strongly mandates that keys for different
algorithm version have different types, and
verification functions MUST reject a key of the
wrong type.Is JWT safe now? Maybe. A lot of issues
have been fixed, but new issues keep coming all
the time. We're not even halfway into this year
and I can count at least the following serious
2026 CVEs: CVE-2026-28802, CVE-2026-29000,
CVE-2026-1529, CVE-2026-22817/8, CVE-2026-34950,
CVE-2026-23993, CVE-2026-32597, just to name a
few. Most of them are the same classic alg=none,
signature verification bypass and algorithm
confusion issues.The issues is that new libraries
are coming all the time and the vulnerability
elimination process for existing libraries is just
a random scattershot. If a security researcher has
happened across a vulnerability in library X and
reported it, it's solved. If nobody has found it
yet: though luck. Unless you pick a library that
has been officially audited for these issues, you
don't really know if it's truly safe. If you use a
PASETO library, it's probably not audited either,
but the chance of it having these common types of
issues (and other issues, like psychic
signatures[2]) are close to nil.---[1]
https://github.com/paseto-standard/paseto-spec/blo
b/master/d...[2]
https://www.securecodewarrior.com/article/psychic-
signatures

  > unscaled
Wow, Fortune 500 companies are using an insecure
technology, get hacked and exploited by cryptominers
and PII burglars and then just patch their
vulnerabilities and call it a day? This never happened
before! /sarcasmJust because a certain practice is
popular, doesn't mean it's good for security, and it
definitely does not mean the companies who do this
never get hacked. Popular != Unhackable. I don't
believe this needs to be stated.Cases in point:-
Passwords limited to 8 characters- Passwords hashed
with a fast, single-iterated hash (with or without
salt, that's not the main point, we are not in 2003
anymore goddamnit, and GPUs are a thing!)- Passwords
stored in cleartext- Using old-style C/C++ without
bounds checking and fuzzing and treating stack
overflow exploits as just a fact of life we'd have to
live with, while most other languages don't get
anymore (and if you have to use C/C++ for reasons
there are ways to prevent this).- Injecting unverified
user input directly into SQL strings.- Using ancient
software without ever patching or updating vulnerable
versions.

  > jzelinskie
Lots of very bad uses for JWTs for service-to-service
communication, too. There are often way more
standard/foolproof alternatives than how lots of
people use JWTs on the backend.I feel like discussions
like these usually will surface PASETO/Macaroons/Thin
Mints as the fix without acknowledging that the
complexity of distributed token passing with arbitrary
attenuation isn't a fit for most use cases.That all
being said, sometimes JWT is the right solution for
the job! It's the core skill of software engineering
to be able to take in all the arguments and tradeoffs
and make the right choice for your scenario.Full
disclaimer: Take what I say with a grain of salt
because I build a project (SpiceDB) that advocates for
a more centralized approach for one of the backend JWT
use cases: fine-grained authorization.

  > Rapzid
Yeah, hasn't it been "best practice" for a decade or
more to treat JWT like a ticket and swap it for a
cookie-based session ID in anything browser-like? Then
you just do all the cookie session "best practices" to
lock it down.

  > swiftcoder
> Please, if JWTs are such a horrifically insecure
standard, go ahead and publish your means for hacking
AWS STS's AssumeRoleWithWebIdentityThe standard and
AWS' specific implementation thereof are two different
things. Can you afford a security org the size of
Google or Amazon's security orgs? If not, you are
playing a different ballgame.

  > jeltz
I agree with your first part but your edit is a logic
fallacy. I don't need to be able to hack something to
say that it is insecure.For example: I don't know how
to exploit SAML but I know it is a terrible standard
dur to making all of the XML parser an attack surface.
I am not a security researcher so I dont know how to
find exploits in XML parsers but I know having a huge
attack surface is bad.

  > jkrejcha
JOSE can still have problems if it's secure when
implemented properly. A lot of API surfaces for them
can kinda suck. If secure when held right was
equivalent to good, then that would apply also to
stuff like X.509There are better alternatives for a
lot of cases, standard session tokens or API keys are
a popular one in use in most major websites online and
work pretty much perfectly for most use cases.I'm not
gonna say those standards are completely without
merit. The best thing about them is that it is some
basic standard on passing stuff around that isn't like
ASN.1 encoded or whatever, to which the tooling seems
incredibly brittle and bug-prone.

  > locknitpicker
> Necessary qualifier: for browser-based user
sessions.True. Still the article can't really make any
case, other than pointing misuses and throwing a few
baseless assertions.In fact, it surprised me to see
such an article featuring so many upvotes.The primary
use for JWTs is to allow resource owners to perform
stateless JWT validation,and then be able to trust a
JWT's payload to perform authentication and
authorization. This doesn't mean what the blogger
think it means. These processes become stateless
because the resource owner does not need to perform
any inline request to be able to tell whether the JWT
can be accepted or not. Meaning:- The JWT is signed
and/or encrypted, and the resource owner can use it's
JSON Web Key set verify and/or decrypt it.- the JWT,
once deemed valid, includes metadata that helps the
resource server determine if the JWT should still be
accepted. This includes timestamps of when it was
issued at and when it expires, a JWT ID to check a
revocation list, and even a few user claims such as
user id, audiences, scope, etc. A resource owner does
not need to perform any request inline to perform
those checks. The revocation list needs to be kept
fresh but it can be refreshed as a background task. At
most, if the JWT is expected to be single-use, the
resource owner is able to run the nonce/JWT ID through
a denylist.- one of the primary values of JWTs is
performance. For the vast majority of usecases, the
whole verification&validation flow is stateless. This
means no outbound request is needed to execute
authentication and authorization checks. Instead of
plowing through something between 20-100ms of latency
to handle auth in each request, the whole flow takes
less than 1ms.I don't think the blogger fully grasps
this nuance. Outright asserting that JWTs introduce
performance issues completely erodes any trust that
the blogger has a solid grasp on the subject.

  > tptacek
There is in fact a long lineage of vulnerabilities
caused by JWTs in real applications.

  > dheera
Why is JWT so messy? base64(something) + "." +
base64(something else)?Why not just
base64(JSON.stringify(everything)) ?

gwbas1c
I once got the heebie-jeebies about an API and on instinct
ended up protecting an application from the "macOS
Keychain Access Control List (ACL) Zero-Day
vulnerability".This article just sounds like
heebie-jeebies, at best it's someone saying something
about JWT doesn't smell right (because it can be used
incorrectly,) at worst it's a pissing match / religious
war.This article would be more credible if it had a
tangible explanation of a real exploit.(BTW: I don't
understand how cookies are inherently "better" or "worse."
They can be sniffed and replayed too.)

tracker1
JWTs are insecure... even when using trusted, rsa/ppk
based signing methods? not shared secrets.JWTs are too
long lived... Nothing is stopping you from limiting the
JWT lifetime and having a refresh model against an
authentication authority... I mean, even if you use cookie
based sessions, you're storing somewhere... you can have a
jwt valid for 5-15min. 15minutes is roughly the cache
timing for many authorization systems including Entra...
and even a 5min token with a refresh system can be used
fine from a browser.Lastly, I prefer to have identity/auth
separated from the application/api services... it
externalizes context and JWT per request is easier to deal
with than some shared cache/state system that may
intermittently fail as opposed to a signed token that you
can verify the signature against known authorities.

  > owaislone
They aren't insecure really. They're pretty secure by
design but people are using them for things they were
never designed for like web/mobile client auth.

  > hparadiz
You can make a JWT invalid after 30 seconds or even 1
second. You should set an aud (audience) when creating
the JWT. Otherwise the signature is crypto-graphically
sound. Validate every single JWT every single time
with a short lifetime.OIDC tokens are all JWTs btw.

    > > tracker1
If your talking about a browser context, where the
authority is separate from the requesting body,
then expiring even at 30s is excessive for user
context, let alone every 1s or every request...
you're effectively then inflating every single API
request into 2 requests... one for a new token,
then another to the API being called. This is
irresponsible for not much gain in a user-facing
context.

      > > > hparadiz
You should not be using them for user contexts
at all. The cookie should be the session token
and the sessions should be stored on the
server side where you can simply delete them
and the user's login becomes invalid. Using
JWTs for this use case is just plain wrong.

        > > > > tracker1
I disagree with you and the article on
this... I thought that was pretty
clear.You can use a revocation list with
JWT if necessary, and if your JWTs never
last more than 15m you'll be fine.. and if
your security window is tighter than that,
you probably have bigger issues to deal
with.

          > > > > > hparadiz
I think you can use a JWT just fine to
introduce a new user into a system but
once authenticated just set a classic
session cookie with an expiration. I
get when you can't for like an API and
sure that's where JWTs are best. But a
regular website? Not the biggest fan.

          > > > > > tasuki
> You can use a revocation list with
JWT if necessaryYes and let me just
add that in many cases the use case is
such that a revocation list is not
even needed and then JWTs are actually
stateless and it's a small win for
everyone.

          > > > > > stavros
But in that case you're just using
sessions, and the JWT is a
microoptimization to avoid hitting the
DB every request.

    > > beckler
The ID token is always a JWT when doing OIDC, and
it makes sense in this situation.Because the ID
token is not a set of credentials, but signed
information you'd use to create/update a user's
profile.You can technically use JWTs as access
tokens, as the spec doesn't specify a format for
access tokens, but in my experience they're
normally opaque bearer tokens.

      > > > unscaled
The main reason I don't like the id token is
that I've seen way too many instances of the
ID token being used as a trusted identity
assertion sent across multiple services or to
third parties. This is very dangerous, since
ID tokens tend to have longish expiry (several
hours), are not revocable, and generally do
not carry any concept of authorization (e.g.
restricted scope).It would have been better if
instead of implementing ID tokens, OIDC only
supported the authorization code flow and
returned a JSON payload of claims (which
nobody would incorrectly assume to be
trustworthy).

      > > > hparadiz
I prefer saml 2.0 for jit provisioning.
owaislone
JWTs are awesome but they are are being overused by
people. People use them in web, on mobile and everywhere
in between. Places where cookie or bearer token auth has
already solved the problems. JWTs strength is that it can
be verified independently without real-time coordination
between services. So a service can issue a JWT with auth
scopes to a user. The user can take the JWT to any other
service and if that service trusts the signer, it'll allow
the user access and also get basic user info from the
token itself. The service doesn't need to make an API call
to the issuing service or even know if/where it exists.
That is where JWT is really powerful.For web/mobile auth
where same server issues the JWT and same server verifies
it, it makes no sense. JWTs cannot be invalidated. If a
user loses some permission or account gets disabled, JWT
will still be valid until its expiration time. Servers
must either make DB calls to verify the user is still
active or be fine with deactivated users having access for
a while after account is disabled. This completely defeats
the purpose and bearer tokens work perfectly for this use
case.JWTs should _almost_ never be used in client side
auth. Client should send regular cookies and bearer
tokens. The auth server can internally generate a short
lived JWT and inject it into requests before they get
routed to various services internally so those services
don't need to query DB every time to verify the user.

ApolloFortyNine
This links to some other blog post for the bulk of it's
'why', and that blog post mostly seems to be annoyed about
"You cannot invalidate individual JWT tokens". Which every
time I've implemented, the general guideline is to check
for invalidated nonces somewhere. Which resolves that
random blog posts second point too.>The JWT specification
itself is not trusted by security experts.This feels like
it needs more evidence than just one blog post. And that
blog post seems to just largely blame bad implementations?
Something that will plague any standard.Overall, I don't
know what I expected clicking a random gist link.

  > tracker1
Yeah... some early implementations just allowed for
any authority to be set in the header and trusted
it... that's of course wrong from the start... if you
only allow for trusted or "known" authorities a lot of
the contextual concerns become non-concerns.Beyond
this, you can make shorter lived JWTs just fine in the
browser and have the agents self-update. If you use
Azure Entra or a number of other providers it works
this way in practice... you keep your JWTs relatively
short lived (5-15m) and can even check for jti
revokation.JWTs are incredibly useful for
separating/reusing an access authority from your
applications/api systems. You shift the attack surface
and do it in a way that can be trusted. We use PPK for
lots of things, including SSH all over the world. No,
I wouldn't use shared secrets and I wouldn't use long
lived tokens... but short lived, ppk signed tokens
from verified/known sources are generally fine.For
that matter, it's often API keys that are really
problematic. Just had to implement them... for me, the
API key presents as a Bearer token as well, but
there's a short "sak." prefix then an identity part
(base64url uuid bytes) followed by a secret as
base64url bytes... in the database is the uuid and a
passphrase level salt+hash from the secret.. so the
api key generated should be treated as a secret and is
one-way to the database, so a db breach doesn't breach
auth.Even then, an API key leak is far mroe likely
than a problem with a well implemented JWT solution.

  > tasuki
Also many situations just don't require a "Logout"
button and hence don't require a revoked list.On a
linked page, there's also this:> Any JavaScript code
on your page can access local storage: it has no data
protection whatsoever. This is the big one for
security reasons (as well as my number one pet peeve
in recent years).This is a weak argument. You know,
just don't put "any javascript code" on your webpage?
Limit it to trusted javascript code? If you allow
random people putting random javascript on your
webpage, you have already lost anyway!

  > tptacek
Right, but once you're checking for invalid nonces,
your token format is now stateful; it's lost the
primary benefit of statelessness, which is continuing
to function under network partition between the
application server and the token state store.

    > > jstanley
How can you provide a stateless logout (that
invalidates credentials rather than simply
forgetting them client side)?

    > > tasuki
So don't do that - and you're stateless!I can't
recall the last time I used a "Logout" button
anywhere. I no longer visit internet caffees...

      > > > SnowflakeOnIce
Logout functionality is not the only use case
for token invalidation. Another significant
one is to revoke a token that has been
exposed, like inadvertently pushed to GitHub.
In that case you want to invalidate the token
to a avoid unauthorized access to your
service.With vanilla JWTs, you have no way to
do this! But then if you add revocation
checking on top (which people do), your JWTs
are no longer stateless.

      > > > icantevenhold
Every authentication system i ever implemented
(and I worked on many) needed some way to
invalidate sessions regardless whether it's
self service for the user or not.And once you
do you need some state and then JWTs don't
make much sense anymore.
There are of course many valid use cases for
JWT so "JWT bad" is a very reductive take

        > > > > tasuki
It's curious: very few authentication
systems I ever implemented needed some way
to invalidate sessions.

          > > > > > tptacek
You either reissue tokens constantly,
every couple minutes or so, or you
have to reliably invalidate.

  > jotato
> "You cannot invalidate individual JWT tokens". Which
every time I've implemented, the general guideline is
to check for invalidated nonces somewhere. Which
resolves that random blog posts second point too.100%
agree. This is common sense to me and I'm always
surprised to re-learn people don't do this

    > > hparadiz
Not checking the signature on every single JWT is
the same as storing a password in plain text.

      > > > Natfan
worse, it's storing identities in an editable
format that any attacker can use to
impersonate any user, no?

        > > > > hparadiz
Even worse than both of those scenarios.
If you don't check the signature anyone
can simply write whatever they want in the
payload string. The signature is always
generated by combining the payload with a
private key. Then the receiver uses the
public key to verify the signature. If you
don't do that the payload can be modified
to be anything. Storage not required by
the attacker.It's like prompting for a
password but accepting any password as
valid.

littlecranky67
In sessions vs. JWT revocation lists, there is an argument
in favor of JWT revocation lists. JWTs have a limited
expiry timestamp, so you only ever need to maintain a
revocation list for tokens not expired yet. Given that you
probably only have a fraction of JWTs revoked compare to
valid JWTs in circulation, you only need to query a very
small dataset for each request.When using sessions, your
list of valid sessions is probably orders of magnitudes
higher that the revocation list - thus the data lookup
costs and the storage cost of that statefulness is
higher.Plus, the article mentions JWTs are stateless but
that is usually not true. You mostly not only validate the
JWT, but also obtain a matching identity object (i.e. user
details) for each request to see if the user is still
enabled/authorized to do whatever he does. You can
leverage stuff such as per-user revocation lists, or a
minimum_issued_at that will validate any JWT iat field.
This allows the "Logout from all devices" pattern, where
that action will simply set a user's minimum_issued_at
field to $NOW. All previous tokens will thus be revoked,
without individuall revocation list checks.