Hacker News new | past | comments | ask | show | jobs | submit login
JWT vs. Opaque Tokens (zitadel.com)
131 points by fabig on Sept 29, 2022 | hide | past | favorite | 98 comments



My biggest problem with JWT is that it's way overhyped, leading less experienced devs to believe JWT is the only way to implement auth, unless you use a 3rd party provider.

JWT is a solution to a problem 95% of us don't have: https://apibakery.com/blog/tech/no-jwt/


To add to that, even if you have microservices everywhere on the backend, JWT is still rarely a good choice. Those services are typically exposed to customers via some sort of API Gateway/BFF. So if your clients don't speak to microservices directly, authentication between those services could easily be done via plain old HTTP Basic Auth.

Stripe has been doing that for their public API since forever, and that's one of the big reasons why it is so easy to work with them.


It's worth noting this coconut pattern (hard auth facade, soft interior), while popular and helpful for dev efficiency, is a weak security practice. Using simple pre-shared keys between internal services excludes any meaningful access controls over user data. Whether that's important depends on the domain.

For stripe, though, I'm surprised they wouldn't want to ensure internal data requests are being made on behalf of customers who should actually have access to the data.

Also, for bigger companies (although, why not smaller ones?), you hit a point where you can't keep blindly trusting every service. Better to have internal security controls.

As for when these tradeoffs are appropriate, hard to say. But a good rule of thumb could be when the product is big enough to need an SOA.


> As for when these tradeoffs are appropriate, hard to say

And that is my biggest problem. If you don't know whether you need something, you don't need it.

Or you just don't have enough expertise to understand why you need it, but that means you will implement it the wrong way anyway.


> If you don't know whether you need something, you don't need it.

That's a bad approach for security.

Starting with the solution is the wrong way to do it, but you certainly need to actively look if you have problems.


Let's say you're a startup that's hiring it's 5th engineer.

Should you do criminal background checks? Install spyware on laptops? Forbid hiring from other countries? Restrict developer access to production env? Invest all your development time into security features until you run out of those?

If you actively look for problems, you will justify all of those.

Security is not about knowing which problems exist and fixing them all. It's about knowing which risks are acceptable at your current stage. If you don't know which risks are acceptable, applying random security practices is worse than no security at all. At least when there's no security you are aware of it.

If you can't come up with an attack vector where HTTP Basic Auth causes significant problems, you don't need JWT to secure communication. And even if you can come up with an attack vector, is it really easiest one to execute?

Speaking of attack vectors, how many of the folks that use JWT for inter-service communication actually rotate encryption keys when anyone who has access to them leaves the company? My bet is very few.


Why do security people think security trumps everything?

It's a serious question. As time goes on I more and more land on the side of Linus Torvalds wrt to security.

It kills me the amount of stupid shit I've seen done in the name of security.

If security people were to come up with driving standards, no one would be allowed to turn left because it's more dangerous then turning right.


Ironically, authorization downstream of an API gateway is an application that JWTs are a good candidate for.


You have different approaches of security depending on your desire for risk mitigation. Examples (with somewhat made-up names);

1. An ingress moat - your ingress makes decisions on whether someone should be allowed in, after which internally anything goes.

2. Two-stage auth - building on top of the above, internal messages need authentication/authorization for communication channels to be established. This might be basic, x509, kerberos, or federation-based protocols. Microservices environments might use SPIFFE for this.

3. Messenger auth - in addition to the above, requests need to show evidence of direct/indirect interaction with the parties involved. For example, an access JWT may be sent around since it would provide indication of a user and tenant.

4. Intermediary-authorization - a variation of the above, evidence is required on each communication channel 'hop' that the initiator(s) are authorized to make the request against a service. This may involve more centralized services to create many opaque/integrity-protected authorization decisions from an external systems view.

5. Transactional auth - another variation of 3, evidence is required specifically that a given action is authorized, including parties and potentially parameters. This might be sent directly from the original initiating party, or may be created for internal systems close to the ingress point.

Note that for a service with a public API, all of these can hypothetically look identical. However, once you get to a certain amount of internal authorization checks, you naturally start to try to represent those with integrity-protected stateless tokens to avoid needing a massive centralized system with low latency consistency and high uptime needs. OAuth access and refresh tokens were designed to help there.



Yes, but what developers want from JWT is:

1) not having to worry about authentication in their app (and not about security issues, exploits, account recovery, fake accounts, bots, ...)

3) the ability to use it in any framework they want (e.g. the Go standard library "framework")

4) the ability to "upgrade" to full microservice authorization

And yes, these are sort-of kind-of not-quite-but-almost provided in Django, Spring and Ruby On Rails, but if you want to use whatever framework you want ... JWT is definitely the closest thing available, or at least it seems so.


> This brings us back to how we implement authentication in Node projects you generate with API Bakery. If you've read all of the above, no surprise here - we use bearer tokens.

WTF? They just spent the whole article talking about how bearer tokens were stupid (everything they said about JWT applies to all bearer tokens). And then they say "instead of this thing we said was stupid, we use the exact same thing but named differently". WTF?


Nonsense. Issues like inability to revoke JWTs don't apply to bearer tokens where the API server needs to query the token issuer to found out who the user is (which can then implment revoking the token simply by dropping the record for the token from the DB, all further calls will fail the lookup!)

Remember they stated:

> Major advantage of JWT compared to bearer tokens [...] is that they don't require looking up the token.

Other problems with JWTs vs opaque bearer tokens they listed include:

- increased attack surface due to complexity of JSON parsing/validation

- misconfiguring JWT libraries to allow no signature

- leaking data to users because you thought JWTs were encrypted

Now you are right that some of the complexities involved with JWTs like refresh token can apply with arbitrary bearer tokens, especially if you want to limit the lifespan of the access token, but those are implementation choices. Technically you can just have a single long lived bearer token instead of a long lived refresh token, and a short lived access token.


Hold on, this is backward. They said:

> You should not use Thing X because the advantage it has over Thing Y is not an advantage. In fact both Thing X and Thing Y have the same disadvantage. Except that Thing X has a workaround and Thing Y doesn't. But that workaround adds (gasp!) complexity! Obviously, we only ever use Thing Y and you're a bad developer if you use Thing X.

It's smoke and mirrors. They never give an advantage of bearer tokens over, well, JWT barer tokens, which are a type of bearer token, the thing they said they use. They just said JWTs don't actually have one specific advantage, except when they do. That's not an argument against JWTs!

> increased attack surface due to complexity of JSON parsing/validation

People who use JWTs are already parsing JSON everywhere. It's not hard. If it's not valid JSON, clearly you don't authenticate.

> misconfiguring JWT libraries to allow no signature

You can misconfigure anything. You can misconfigure your server to not actually check the bearer token. Why is it more likely you'll forget to check the JWT signature than the bearer token? This is something you write one time and never touch; it takes an afternoon at most.

> leaking data to users because you thought JWTs were encrypted

Yes, you should not put sensitive data in a JWT, encrypted or not. This is a fair criticism.

> Technically you can just have a single long lived bearer token instead of a long lived refresh token, and a short lived access token.

Which, as they pointed out in that article, is a disadvantage of long-lived bearer tokens.


Like most web stuff and the blockchain, JWT sound fancy but it is a very trivial thing.


Full disclosure, I work for FusionAuth, a competitor of Zitadel in the auth server market. Our software issues a lot of JWTs.

I agree with the premise of the article, which is that JWTs aren't the right answer for every solution. Ine pattern we've often seen to mitigate some of the issues of JWTs is to store them serverside, in a session. Now you get all the benefits of session management (revokability, single view of usage) but can still present a JWT to other APIs if needed.

I'd never put sensitive data in a signed JWT. In fact, at FusionAuth, even JWT claims like the user id is a UUID, just so you don't inadvertently leak information like "how many users are there" via an integer user id.

The main reason why JWTs work so well is their statelessness. By that I mean the fact that you can verify their integrity without "calling home" (especially if you've set up asymmetric keys correctly). In this case, you don't have the same availability and scaling requirements for your server that issues JWTs that you would for a server that was responding to opaque token requests.

It's from last year, but I compare and contrast these two approaches in this video I presented at a meetup, starting a few minutes in: https://youtu.be/rArCF7nUcvY?t=451

JWTs are part of building an app that scales. But like any other scaling choice, it has consequences. Just as you wouldn't shard a database before you need to, you shouldn't build an app that uses JWTs to scale unless you need to. Though switching between sessions and JWTs is going to be an easier transition than sharding vs unsharding a database. Data has inertia and all that.

I don't believe there is much functional difference between sessions and opaque tokens. Both have to be stored somewhere, both require a central source of truth, both require that store of truth to be constantly available. There are differences in how they are managed/acquired, but from an architectural perspective they are similar.


> …pattern we've often seen to mitigate some of the issues of JWTs is to store them serverside, in a session.

> The main reason why JWTs work so well is their statelessness.

You realise this is obviously a very weak argument for using them? Stateless in a way that is apparently not useful for most people. Great.

I’ve never seen a serious JWT implementation that didn’t use a database to manage lockout and other state related things that inevitably turn up as product requirements.

Once you do, you now have a stupid pointlessly complicated session implementation that potentially leaks sensitive information.

That’s the whole point of the article.

If you you need jwt for “other services” issue jwts for them when they become a requirement.


> You realise this is obviously a very weak argument for using them? Stateless in a way that is apparently not useful for most people. Great.

Well, it depends. With JWTs in a session, you get a time bound set of credentials that can be verified server side, with rotation built in and a lot of docs, libraries and experts thinking about things. (Regarding that last, there's a lot of brainpower put towards edge cases and security in the OAuth working group!)

Other sets of users use JWTs in their pure stateless form, often with a short expiration time in a secure HTTPOnly cookie, transparently refreshing often. This offers easy horizontal scaling, as long as you only want to talk to servers in a single domain (you can always proxy calls other domains if needed).

I don't think that JWTs are a magic bullet, but they definitely have some strengths.


> I agree with the premise of the article, which is that JWTs aren't the right answer for every solution. Ine pattern we've often seen to mitigate some of the issues of JWTs is to store them serverside, in a session. Now you get all the benefits of session management (revokability, single view of usage) but can still present a JWT to other APIs if needed.

This seems weird to me. The whole point of the JWT is that you know with confidence that the client has the data you need and cannot change it. I mean, if it works for them cool, but at that point you might as well just have a session with an ID and a table with all the relevant session information in one swoop.


> The whole point of the JWT is that you know with confidence that the client has the data you need and cannot change it

I would change that to "The whole point of the JWT is that you know with confidence that the client can provide tamper proof data, often identity, to other servers and APIs."

Yes, the OIDC id token is for the client to consume, but if it is an access token, it is for the resource servers (to slip into a bit of OAuth jargon). Put whatever you want in the id token, knowing the client will have full access to it. If it is a javascript or native client, you don't know where that will go, but having it shouldn't grant access to anything privileged.


> Regarding that last, there's a lot of brainpower put towards edge cases and security in the OAuth working group

This is obviously why people should adopt OIDC.

I don't really get the hate for OIDC. Nothing says "Junior Developer" like posting on Hacker News how shitty this widely adopted, durable technology is, especially by calling it "JWT."

This token expiration boogeyman is especially dumb. If there's a security misconfiguration so disastrous that the user can exploit it in less than 5 minutes (a typical access token lifetime), you're only going to be booting them out of the system after they've done the damage anyway.


…but how do you get the access token every 5 minutes?

Surely not, because, it’s be obviously stupid using a refresh token right?

Not a long lived refresh token.

A refresh token… that you might, say, need to revoke at some point?

Somehow.

Definitely a boogeyman. …because people don’t understand how it works.


> Somehow.

This word, revoke. It appears in OIDC exactly twice. You can look it up yourself. In order to get a new access token, you have to hit an authorization server URL, not an application server URL, with your refresh token. If it was revoked, you'll know then. The authorization server will simply not give you a new access token, and while you have a string of bits still called a refresh token, it is as useless as an old password.

I get that your snark is stylized, and this is Hacker News, and uniformly, the junior developers in my life that I engage with deploy snark, that's all fine. Really the emphasis is on how much the OIDC people have thought about all these things, and while I agree it is a lot to learn, it is in principle the only winning standard for authentication and authorization nowadays, it is what everyone uses, so you might as well learn it.


I suspect cookie sessions is still more widely used than anything approaching OAuth and it's ilk. Hell, even JWT's often get associated with cookie sessions, which helps mitigate the revocation problem.


UUIDs leak information, by definition. For that reason, I'm not a fan. It's just leaking on the other side.

I'll explain: A UUID should be pretty unique in the universe. Wherever it crops up, as there should always be an assumption of the possibility of leaking data and associated data at the same time, whomever the UUID belongs to has had their information leaked. That could be associated meta data in a request like IP address, to a print out that has a user's UUID in the header. In all cases it ties data to an individual.

Better is to just use a high integer that doesn't simply increment, but changes by an amount. But low enough to blur into noise. Heck, this can be rotated.

I stand to be convinced otherwise, but via the reasoning above I believe UUIDs are not desirable for user privacy.


I don't understand what you mean here.

You're saying that because UUIDs can be assumed to be unique in the universe, when it's leaked alongside other data it can then be correlated with other data leaks to tie data to an individual? How does a user ID being `8646846` make it any safer from leaks or correlation than `123e4567-e89b-12d3-a456-426614174000`?


I investigated this issue with a customer recently with a focus on revocation. We concluded that if we have to hit the database to check if a JWT token is still valid we can use a session cookie (or equivalent) and hit the database to get the user, the associated capabilities, etc.


Another drawback for JWTs (when used fully statelessly) is the inability to list active sessions on other devices (which may lead to revocation).

That being said, always going to the database for connecting an opaque session token to an identity can quickly become slow, and if those features listed above are not desirable, having a blocklist of revoked JWT IDs in an in-memory cache (like Redis) can bring back some performance benefits.


> having a blocklist of revoked JWT IDs in an in-memory cache (like Redis) can bring back some performance benefits.

Doing this obviates some of the benefits of JWT's statelessness, but for situations where revocation is really important and you can't have that few seconds of JWT validity after a user logs out, this totally works.

My employer has an article about this topic here: https://fusionauth.io/learn/expert-advice/tokens/revoking-jw...


> always going to the database for connecting an opaque session token to an identity can quickly become slow

PHP does this every single request. I’ve never had enough users that this became a significant issue (and you probably don’t either)


Not all requests are created equally, maybe you don't check jwt revocation on some high throughput read endpoints but on updates or reading of sensitive data you do check that list.

with JWTs you have the flexibility with the opaque you don't.

JWTs also allow you to do client-side logic on things like entitlements but then verify against database when the user tries to view something


You still can add a post response filter to update some statistics table after you send out the response to the customer to keep latency low and pressure on db low too.

Still very few companies need something like this.


> always going to the database for connecting an opaque session token to an identity can quickly become slow

If select by id or token is slow for you then I worry about the rest of the application quality. Probably much slower elsewhere that the identity lookup is the least of your concern.


Exactly this! If you are hitting an authorization server via the introspect endpoint, why not simplify things even further and just hit dynamodb or redis or database and check the value of a cookie?

A few reasons why you might want the opaque token come to mind:

* You want the OAuth ecosystem (the libraries, the scopes, the user permissioning)

* You are being forced to use an opaque token because your user authenticates somewhere else, and they only provide you an opaque token.


Or you want to share sessions across multiple domains.

This is where a identity service also can help ;-)


Well you want to check your session cookies as well against something ;-)

Surely you could trust a signature and lifetime but that makes the cookie no different than a JWT (only storage wise the differ in that case).

Generally speaking it is recommended to use opaque tokens and rely on the userinfo / introspect endpoint call to make for the session management.

Only in specific cases where latency and/or scaling might become an issue you should opt for JWT. At least this is my opinion.


> Well you want to check your session cookies as well against something ;-)

I think that is the point of the op: If you have to check a db to realize revocation, then you can use session cookies because they also need to hit the db. (Because you don't get rid of the db hit for using jwt).


Well, that certainly depends. One accesses the db on every request, the other accesses the db on every correct request (and the db is smaller).

As a rule, there is no difference. But if you are one of the exceptions, those can have differences of orders of magnitude.


Than you don't need it if you don't see the advantage.

You normally do lots of API requests while you are logged in. To services and to download an image for example.

You make sure the current jwt is valid for a few minutes and hit only the database with the refresh token for example.

Only in worst case you really need to block a token and it might be much easier to sync those few tokens in your system into some local cache and let them expire automatically (because you know when they expire as it is contained in the token).

But yes if all of this sounds complicated, use sessions and a distributed redis for your session or just the database.


Lots of messy terminology use here. Example:

> In addition, since [opaque] tokens are stored on the server, they can carry more data than JWTs, and you can easily revoke them if necessary.

Opaque tokens don't carry _anything_. THey're just an identifier used to query a database inside your application. The article repeatedly implies there is meaningful content inside an opaque token.

More messiness:

> Opaque tokens cannot be read by people that hold them since they are undecodable strings. A client cannot read the contents of the token

Sure they can be read. It's just that the totality of the content _is_ the token itself.


> Opaque tokens don't carry _anything_.

Opaque tokens are opaque to consumers. There’s no limit on what can be in there, as long as they’re opaque. So yeah, you don’t need that token to just be a PK. It can be a real payload (generally encoded).


> Opaque tokens are opaque to consumers.

> It can be a real payload (generally encoded).

Based on your definitions, a JWT is therefore a specific kind of opaque token. I don't think it is personally.

I think we really should be talking about tokens as falling into two categories, of which JWT is an implementation of the first:

1) stateless - the token contains meaningful content that can be verified by a consumer. JWT is such an implementation with a standardized way of verifying the content without additional database queries (excepting revocation here)

2) pure identifiers - the token is merely an identifier, and all consumers must reach out to some other service (eg redis) to retrieve the details/capabilities that the session should have.

Edit: alternative proposed terms: content-full, content-less


Opaque token is called opaque for a reason - only the issuer knows what's in it, whether it has content or not, is it an identifier or an encrypted payload, what to do to make use of it.

Content-full and content-less break that abstraction.


This is a hard area to work on, everybody that says something apply their own meanings to the words, you can never know what anybody else says.

Anyway, I have always understood it as the token being opaque for the verifier too, that only ever tested it for equality. The token that is opaque only for the holder is normally called "encrypted". It may not be opaque to the issuer, but that doesn't change much.

But anyway, depending on what you read, you will get different meanings.


> Anyway, I have always understood it as the token being opaque for the verifier too

Which falls flat on its face the second you realize the "verifier" can pull out information based upon that opaque token.

It just means the intent is not for the person receiving it to do anything except reflect it back on subsequent requests. That's it. Typically it's done by handing something that is meaningless without context, said context not being available to the consumer being issued the token. But there is absolutely nothing that says you can't hand them a JWT meant to be used as an opaque token. If you're really worried about it, encrypt that bad boy before giving it to them.


My biggest problem with using JWTs for authenticating a SPA is where do you store them so that a user does not have to login every time they visit your application? Every SPA tutorial I have seen says to throw them in the browser's localStorage. Well now you just opened yourself up to XSS vulnerabilities. Any code running on your page can access localStorage and make requests to ship the tokens anywhere they would like.

I prefer session cookies for web applications. Sure you have to worry about CSRF, but that is easily solved with CSRF tokens. Furthermore, is CSRF even really an issue when you are using a JSON API and have CORS properly configured.


> where do you store them so that a user does not have to login every time they visit your application?

We recommend HTTPOnly, secure cookies for storage with an SPA. Diagrams here: https://fusionauth.io/learn/expert-advice/authentication/spa...

If you need to access APIs from elsewhere, run an API proxy server side that can validate the JWT and then forward on the requests.


Adding a CSRF middleware to your app is something that you need to do once, ever.


We moved from jwt to opaque tokens and it's been fantastic. We also moved from using redis as our token store to using postgres (aurora).

Querying against over 100M on postgres adds one or two millisecond p99 latency to all requests. That's perfectly fine. In fact, that's about the same performance we saw with Redis. Both were over the network, so I presume the network accounts for most of that delay.

Having them in an rdbms is so convenient. It makes customer facing feature like enumerating current sessions very simple. It makes token expiration and remote logout simple. It makes spotting weird bugs much easier.

I can't recommend it enough.


Whenever I see jwt I always think of tptacek's arguments

https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...


Thanks for the link. tpacek makes useful arguements.

I've come to like JWT's for mundane, tiny projects because I can use https://www.keycloak.org/ and get a lot of boilerplate account stuff for free -- password reset, registration page, eula gate, et c.


Well you can have the turnkey of keycloak and JWT optional with ZITADEL ;-)


Unless I misread I consider this statement not true or contradictory:

  it has all the information the server needs except the signing keys, so the server doesn't need to store this information server-side. This means that users can get a token from your authorization server and use it in another without those servers needing to consult a central service.
In order to not have to care about rotating keys, a typical Resource Server would fetch the public key from the SSO server. Not necessarily in real-time, but at least on a frequent periodic basis. It even says "except the signing key", which is half true, since the public key needs to be well.. public and available.

Now this may be nitpicking but it comes with an important implication: Your jwt validating party needs to have network access to the SSO service (unless you want to provide keys for token verification yourself, which I do not recommend), which is not always a given, especially in hybrid setups (On-Prem + Cloud).


Well true, public keys would be better wording wise.

One thing I just wanted to append to this here:

> Not necessarily in real-time, but at least on a frequent periodic basis.

Periodically fetching the keys is not really a best practice on its own. The consumer (RP) must be able to handle newly published keys at runtime. Since a JWT includes a KID it is recommended to lookup a local cache pre-filled from a scheduler while fetching new KIDs on demand.


> In order to not have to care about rotating keys, a typical Resource Server would fetch the public key from the [authorization] server.

Another option would to be prepackage N public keys with the resource server during deployment. This means there is absolutely no network access required, just filesystem access to the public keys.

You want more than 1 so that you can rotate the signing keys if you need to, without redeploying said service.

What is the correct value for N? I dunno, depends, but it's not hard to generate 100 public/private keypairs and then bundle up the 100 public keys into a deployment artifact.

Is this as common as using JWKS? No, not in my experience. Is it possible? Absolutely.


Indeed. We validate Azure AD tokens for our native application. Not all of our on-prem installations have unrestricted internet access. So we have a task that periodically checks and downloads Azure's signing keys[1], so we can distribute them to our customers.

[1]: https://login.microsoftonline.com/common/v2.0/.well-known/op... (jwks_uri element)


Maybe it's about "can get" vs "should be allowed to get"? They can carry whatever so it's technically true, right?


It was more about the

  and use it in another without those servers needing to consult a central service.
part. Unless you take care of key distribution yourself (like another poster wrote), this consultation of a central service has to take place to exchange key information (but not for additional authentication or authorization information).


An ex-colleague loved JWTs, using them in systems where I'd argued strongly they were inappropriate, and since I wasn't able to cut holes in the resulting system in a few hours spare time spent tinkering and they really wanted to do it, it went ahead. I guess I at least know they didn't fuck up in any of the obvious ways because I checked those.

Recommended thought experiment: Every programmer who makes or consumes the tokens in your system needs to attend a meeting about the tokens. Picture that meeting in your head. Was it a huge endeavour, involving several hotels and many months of planning? Then you probably need JWTs. If the meeting could happen in a coffee break then JWTs are wildly inappropriate.


I feel people come down too hard on JWT. If you do just two things there’s basically no difference between sessions and tokens.

  Don’t use it for storing sensitive data
  Expire often


If there is no difference, then what is the advantage of JWT?


Mostly the fact you can check their validity without going to the database.

If you have one you can keep using it without extra network roundtrips as long as it doesn’t expire.

Only the validity of refresh tokens (for generating new access tokens) is checked.


That they've been around for long enough for proper implementations in all major languages to exist, their pitfalls being documented and a lot of devs have some familiarity with it.

With that they are a better choice than most homebrew/framework-specific solutions.

There are some other established (/"standardized") solutions, which might fit your use-case better though, but most of them lag behind JWT in implementations/dev familiarity.


Spot on. Even better if they used a better serialization protocol than stupid JSON.


JSON really is the worst serialization, except for all the others.


What's wrong with JSON?


It requires a complex scanner with a stack to track nested data structures.

Not good for security nor speed in the use-case of a auth token!


It doesn’t have a native date type. And you cannot have comments.


Where I've seen this turn into an argument is when people disagree about whether authorization info is "sensitive." Is it okay to send a list of roles or other authorization info in an unencrypted JWT? Security-wise this seems like it adds risk, but people often argue that it's a good trade-off in order to avoid a round-trip to an auth server (probably with a caching layer in front) for every call.


I figure unencrypted should be useful to the frontend, but use encrypted for authentication or authorization.

Unencrypted: maybe a user preference, or first name, or something that adds value but does not overlap with auth. Like if a frontend could serve the same functionality across three departments but the styling is different, the token's unencrypted claims could determine which style set to use.

Encrypted: user uuid, roles, maybe other known settings, things the backend can handle. Someone stealing a token will just try it anyway whether they can see claim or not, but because it's encrypted it will get decrypted so it's also a different logic flow than unencrypted and could trigger a process i.e. did it come from an accepted ip address or range? Does the ip match the previous? Does the ip match the other known ip for a websocket? So also in this sense we wouldn't want anyone to know what else we might check.


The front end almost always needs to know what a user is authorized to do, because it is reflected in the UI. Which edit/delete buttons are visible and active? Is there a link to the admin page? Is there a link to a supervisor dashboard? Most apps send that information to the front end in a way that maps 1-to-1 to how they model authorization on the back end, often using exactly the same language, in which case you don't gain much by encrypting it in the JWT. But if you are taking measures not to leak your authorization model in the front end, then it makes sense not to expose it in an unencrypted JWT.


> But if you are taking measures not to leak your authorization model in the front end

How would you do that? At the end of the day the front-end needs to know what you can access somehow.

I guess you could add a compile step that rewrites all your permission checks into validating opaque uuid’s.


Say you steal the token, what are you going to do with it? You could argue that you can now iterate all the tokens you steal, decode all of them, and find the ones with admin permissions. But if you have a frontend that wishes to know user permissions/access, you already have an endpoint for that too, so they can call that with all the tokens instead.

The thing is that you cannot change the payload of the token without invalidating it, so you couldn’t set the role to admin and then still have the server accept it.


There's no concrete exploit that it enables, but in principle it's always a risk to leak information about your security implementation. My opinion is that it's a very low risk for almost everybody, worth trading away for extra reliability and scalability if you need it.

To answer the question in your other comment, the back end can, in principle, provide a boolean yes/no value for every authorization-related decision the front end needs to make: can_change_order_status, can_view_supervisor_dashboard, can_view_all_conversations, etc. That way you never leak the model (such as roles) underlying those decisions. In practice, I've never seen anybody bother. Instead they have the back end send the front end values that map 1:1 to the back end authorization model: is_supervisor, is_account_admin, etc. That's why I think for most applications it doesn't matter if users roles are encrypted in the JWT.


And then most people hit the db anyways, since they use the jwt as a session cookie anyways.


Don't roll your own JWT implementations for client side applications, for the love of dog don't.

Story time.

Our company recently switched to JWTs for our SPAs from regular OAuth2. I told them up front - we need a way to invalidate a token if an account is compromised. The lead on this initiative said we can blacklist any token. I told him that's not practical because you would need to know the exact token to be able to blacklist and because we don't store them in the database and don't log them have any real way to figure out which token is being used by an attacker and which are not that it's basically useless.

I suggested a simple fix: cypher the private key we sign the JWT with the hashed password of the user account we're generating the token for. If an account is compromised, we can reset the password and invalidate all tokens for that one user.

I was ignored and lo-and-behold within a month CTO and tech lead are trying to track down JWTs for a hacked account. I told them we can update the app to cypher the key, it'll invalidate all tokens and people will have to log in again but at least they'll be secured. Nothing has been changed to this day, despite banging this drum for literally over a year. We've recently extracted our authentication into a micro-service (for no reason really, i'm still mad about this too) and still nothing has been done about this issue when it would have been the perfect time to fix this.

I love my job but "priorities" and "business cases" as an excuse for this kind of incompetence is rage inducing.

The point I'm trying to make is. You are most likely using some kind of web framework that can just plug in an authentication implementation, just use that. NIH is very real and it is more than a waste of time, it can be dangerous.


Something that never comes up in these discussions is services with built-in JWT validation, like AWS API Gateway HTTP APIs. If you're already using one of these, you might as well take advantage of JWTs in addition to your scheme of choice, whether that's opaque tokens or something else. Let the service provider worry about unauthenticated people banging on your APIs.

You can even maintain statelessness with opaque tokens if you really need it, e.g. issue both a JWT and a session cookie bound together by some shared value, say Fernet tokens. One goes in the JWT in local storage (anti-CSRF), the other goes in an HttpOnly cookie (anti-XSS), requests still require both to decrypt to the same value after the service passes them through.

Generally this is overkill, though. JWTs for the service and your preference for the app works fine decoupled.


What's the difference between an opaque token and a cookie that has a single session identifier in it? 'y know - the way we did it in the 90's.


Cookie is a mechanism to store and send tokens to the server, which is orthogonal to what the token is.

You could store JWT in a cookie, or use opaque token in a HTTP header, for example.

That said, the session cookies we loved back in the day were usually opaque tokens, yeah.


There isn't one - the session identifier is an example of an opaque token


For a normal web app, not too much.

As sibling comments point out, an opaque token can be stored elsewhere (though, to be fair, the session identifier which is in that cookie can be placed elsewhere too).

Cookies are limited in where they can be sent (https://developer.mozilla.org/en-US/docs/Web/Security/Same-o...).


The cookies is mainly used to identity the user (e.g. his session and prior authentication), while the tokens are used to forward something to proof that the an application wants to access one or multiple apis.


So if you need the client to pass on sensitive (authentication/authorization) information to another party without that party having access to the original provider of that data (except its public key, I suppose). Then JWT is a usable format, with a lot of support.


Well, yes ;-)


As this applies to access tokens, if your application doesn't need a JWT, it shouldn't care whether the authorization server returns a JWT or an opaque token. On the flip side, if your app needs a JWT, then the authorization server must return one.

Revocation is either offered by the authorization server or managed by the app. If the authorization server manages it, then JWTs vs. opaque tokens are not a concern because the authorization server issues and revokes its own tokens. If the app manages it, then generally it does so based on the token type. If the app revokes based on opaque tokens, it can handle any type of token, including JWTs. If it revokes based on JWTs, then JWTs are required.

Beyond that, the only differences between the two token types are size and data leaks. Size rarely matters (hehe), so just ignore that. Data leaks are only an issue if you app is leaking JWTs, which is usually considered a critical vulnerability. Remember that access tokens are the main units of identity and if I steal an access token, I effectively become that user/client.


It's funny to read about all the web app usage of JWT, and here I am dealing with it server side.

For those unaware, JWT has the 2 ways of encoding it: JWS and JWE. Because of these properties, you can use it how people would use it for application level messaging. If you are already on a secure channel, you can sign or encrypt the message so application level checks can happen.

This makes it act like a replacement of PGP for you paranoid people out there.

(Btw, I recommend against this, and think people should use PGP or Tink, as it's easy to footgun yourself with JWS.)


Session tokens need to be stored on the server (redis, db, etc..) so you can change them, see all devices logged in, and other requests product will have.

They should be unguessable and unrelated to anything. Read 20 bytes from /dev/urandom (or whatever stdlib your language provides), create an entry in your store for token->user_id, then put it in a httpOnly + secure cookie (so Javascript can't access it), and send it to the client.


Whats the drawback for just including the ip in jwt and revoke if either time is up or ip change ?


I would imagine it would be a very poor user experience. Especially on mobile, where you jump around 4G/5G/WiFi networks and get a new IP address each time.


or if the user allows JS in their browser, then just do some GPU/CSS/Font Collections,etc. finger printing and get the unique id, since all the major bowser vendors are not fixing this problem (except may be for webkit).


IP can change multiple times when switching between mobile stations and wifi hotspots. Plus IP may change for each new TCP request if your operator uses CGNAT


you can have multiple IPs at the same time.

The "IP changes" is a common fallacy - even the mobile IP depend on the datacenter(s) process it. There is no grantee there will be a designated aliasing, and effectively there could be multiple IPs for the same client interleaving.


Best things would be webauthn were you have crypto hardware like your smartphone


The IP can change, is not guaranteed unique (many people can be behind 1 IP). Imagine unstable mobile connections going over 4/5g or random wifi as you move.


I came here to see what in the hell James Webb Telescope is not dealing with some opaque stuff on the universe and I got decepted


I don’t have experience with JWTs, but if you can encode data, why couldn’t you encode a “freshness” time stamp and just invalidate the token after an elapsed time. Does this compromise the encryption?


You can. Usually it's an "exp" value that you set when the token is issued. The time can be whatever you want, I might do something like now + 15 minutes. Then when verifying the token you can check if exp <= now kick out a 401 response.


You can do that. A related problem: there is no obvious way to revoke early (ie. before the timestamp that was encoded when the token was issued). There are solutions but they require building extra mechanisms.




Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: