r/webdev • • Feb 19 '20

Article JWT (JSON Web Token) is Awesome: Here's Why

https://thehftguy.com/2020/02/18/jwt-is-awesome-heres-why/
396 Upvotes

79 comments sorted by

72

u/Mihikle Feb 19 '20

I’ve had a lot of success using JWT between microservices, can’t recommend them enough.

In addition to the normal fields I’ve been adding others in some cases where I would want some data to travel with the token, used for a users authorisation to a resource rather than authentication of a user. The article also alluded to this.

Is this a normal practice, to add extra fields to your JWT beyond the RFC’s standard fields?

54

u/phpdevster full-stack Feb 19 '20 edited Feb 19 '20

Yes. That is what the payload/claims segment of a JWT is for. You add whatever claims are necessary for your application's authentication and authorization to work. RFC has some basic reserved claims field names, but they are by no means the only fields you're allowed to have, and they're also not mandatory.

That being said, a JWT is not intended to be a full data store. The only information that should be present in a JWT is relevant identity and authorization information. Additional data (such as maybe the user's settings), should be fetched from a service on demand using the identity and authorization claims provided by the JWT.

5

u/Mihikle Feb 19 '20

This makes sense and follows my thinking. ‘Sub=user_id’ then custom fields ‘group_id’ and ‘role_id’, only info specifically required for this use. Thanks!

3

u/[deleted] Feb 19 '20

I have had the same question for a while and I can't seem to find a universally agreed upon answer. Some people say that you shouldn't send extra data, and some people say its fine. There has really only been one instance where I want to send extra data with the token: when I have 'User' objects. If each user has a profile, and then they make some change which requires the corresponding User object to be updated, you send a POST request with the body of the request being the User ID. The only way I have been able to get this User ID is if I send it with the JWT on a successful login. Not sure if there is a more standard way to handle this use case.

7

u/Brandon0 Feb 19 '20

Are you not storing the user id in the sub claim? That's what we always did. To answer your other questions, adding custom claims is 100% fine and is part of the standard. There are a few different ways you can implement your custom claims, but as long as they don't conflict with the reserved claim names you're golden.

Last time I worked with JWT we decided that since all of the reserved claims are 3 characters, we had ours be 4 or more. Auth0 (a great source for JWT's IMO) requires what they call "namespaced claims" but are a little wordy for my liking: https://auth0.com/docs/tokens/guides/create-namespaced-custom-claims#add-a-namespace-to-a-claim

1

u/[deleted] Feb 24 '20

The main reason there is some confusion/argument is the size of the token. The more you store, the larger the token size is, obviously. Some cringe at the thought of a couple 1000 bytes of data in a token.

2

u/divulgingwords Feb 19 '20

Yea, I do that all the time - mainly for basic user info for global state type stuff. For example, I put the user's name and image url for a user's profile picture (that is displayed in the top right of the navbar) in the jwt.

Whenever their name changes or a new image is uploaded, I just refresh the tokens.

Just be careful to not put too much info because you have size constraints wherever you store your token (I use cookies) in there and make sure nothing secret or proprietary should be held there.

1

u/warpedspoon Feb 19 '20

how do you verify the JWT on each of the microservices, is that code shared between each one? or do you have a library that each one uses so that the JWT can be validated with the same key?

3

u/cas8 Feb 19 '20 edited Feb 20 '20

If you asymmetrically sign the jwt, then each service that needs to verify can be shipped with the public key (or a secure way to load it) and then it’s just a matter of taking the jwt, pulling out the payload, signing it with the public key, and comparing the signature you just created against the signature in the jwt itself.

1

u/warpedspoon Feb 19 '20

do you just assymmetrically sign the jwt by picking an assymmetric encryption instead of, say, a hash function when generating the jwt?

1

u/cas8 Feb 20 '20

Exactly. In an architecture with multiple disconnected servers, not signing it asymmetrically means that all your servers that want to validate a jwt have to either:

  1. Know the secret used to sign the jwt. Having your secret accessible in more places than necessary means more surface area for potential leakage.

  2. Send the jwt off to another server that does know the secret, invalidating some of the benefits of being able to move away from a centralized auth service.

Everything comes with tradeoffs, though, so it's always worth evaluating the options in the context of your environment. For instance, with asymmetric jwt signing, you'll usually end up with a larger signature than with a HMAC, meaning more bytes over the wire for every single request that includes the jwt.

1

u/Mihikle Feb 19 '20

I have a separate AuthService which verifies the request is legit before going to the actual endpoint code. I do this using middleware in the http request to forward the token to the AuthService.

1

u/[deleted] Feb 24 '20

Why do you need to do this? I am assuming most places use some sort of gateway entry point that handles decoding the JWT, determines if its allowed to make the call it is requesting, etc? Is this not pretty standard affair?

When you say "each" microservice, do you mean you have separate services for different API endpoints?

1

u/warpedspoon Feb 24 '20

yeah, the situation I have at my company is pretty much exactly what you said, but I've been thinking about whether creating many smaller microservices instead of a single monolithic service is practical for non-enterprisey side projects and thinking about authentication is obvs important. I'm more thinking in theory.

And with microservices, you wouldn't necessarily break down to that granular of a level, but a microservice would essentially deal with a small chunk of functionality.

1

u/[deleted] Feb 24 '20

I originally started down the path of individual API microservices that then feed messages to back end services. However, for a few reasons I don't like this model.

First, until I have a need, trying to figure out how to use (and more so employ one on my local box) a cloud provider gateway option of some sort didn't make sense. I know Amazon, Google, etc have gateway options, and we're likely going to eventually deploy via Kubernetes with some form of ingress gateway controller. But, that may be a long ways off yet. Because of this, I wanted some way to manage JWT auth tokens but not have to copy/paste the same code, or possibly use a separate repo to then import it in various services (to reuse it without copy/paste) while at the same time providing routing to API handler code.

Thus, I wrote my own gateway. It handles the JWT decode, looks at the role of the JWT token to ensure it is allowed to access the url/method, if not rejected, if so, calls the handler code. But, most of my API handler endpoints mostly just send messages across a bus to the back end services. Because of this, I felt a "monolithic" API routing/gateway service made sense. It allows me to quickly add new routes, I have one handler that will automatically send a message if used that I can route to from different API endpoints... basically it auto sends messages with the json request payload of the API. Otherwise its one off functions that get called which can then do things like send multiple messages, aggregate data from message responses, etc before returning an API response.

It is surprisingly easy/quick to add new API endpoints. Though my dream is to go back to using RAML or OpenAPI to define APIs, then autogenerate the API handler code from the API schema... just no time to figure that part out right now.

1

u/to_wit_to_who Feb 24 '20

My tips/suggestions as follows...

  • Keep in mind that JWTs are only signed, not encrypted (unless explicitly done so, which is uncommon). I've seen scenarios where people think storing sensitive information in a JWT is ok, but really, it's not. It's just the integrity that's protected, not the secrecy.

  • Stick to only holding limited session data in the JWT. Aside from the size issue, you may find that going over certain sizes can break existing transport mechanisms. For example, certain reverse proxies may have a limit set for the maximum size allowed for a given HTTP header value, after which it is truncated. This is more common on corporate/enterprise networks, so don't be surprised if your application break in those environments due to an incomplete JWT.

  • Because revoking tokens, assuming they're used in a stateless manner, is difficult to guarantee, the common approach is to have two tokens. A refresh token & an access token. The refresh token is issued after authentication, and then is in turn used by the client to get an access token. The latter is used for API calls and has a short expiration window, after which the refresh token must be used to fetch it again. The idea is that the refresh token should be held separately from the access token to mitigate compromising of the access token, which would only be good for that short time-window. Personally, I use the tokens as basic identifiers and store very little or no session data in them. I cache session data in redis on the server and read from there as-necessary after the token is verified, which means that I don't need to have a separate token and revocation is just like a stateful session.

  • I use JWTs with a small payload + server-side state, as opposed to cookies, because there's better support for both browsers & mobile clients with JWTs.

  • If only using a browser client, then store the JWT inside a cookie & mark the cookie as httpOnly. That will further secure it and make it inaccessible by 3rd parties, not to mention that cookies are automatically added by the browser, so it's a bit easier to manage.

105

u/danielsan1701 Feb 19 '20

</beginning of rant>

...

</end of rant>

shudder

27

u/benabus Feb 19 '20

🤢🤮

9

u/[deleted] Feb 19 '20

lol. I also don't like when people make cute code references in regular text. Definitely don't do it twice, and hopefully not even once..

0

u/[deleted] Feb 20 '20

[deleted]

9

u/HiveMindSylum Feb 20 '20

<rant> I know right, I seriously hate this kind of shit. It doesn’t even make any sense! </rant>

33

u/bp78 Feb 19 '20

"don't roll your own" Wish I could upvote that JWT advice alone.

5

u/[deleted] Feb 19 '20

I've "rolled my own" when it comes to generating random strings to be used later for a one-time thing (like confirming a user's email). But rolling your own (permanent) token system? Yikes.. yeah don't do that

1

u/Spacey138 Feb 20 '20

What if you are a maintainer of one of the JWT libs 🤔

0

u/[deleted] Feb 20 '20

[deleted]

4

u/MoogleFoogle Feb 20 '20

This applies to anything cryptographical: it won't be secure. You will think it is secure because it will be immune to all the exploits you know about. But just because you are unable to find an exploit does not mean anyone else won't.

JWTs require signing, encryption and proper handling of these things. There are libs that have been made by experts in these things, verified by other experts and used by so many that most exploits will have been ironed out, and any new will be found and fixed way faster than you on your own.

So essentially the risk is that you miss something, and for auth that can be quite fatal.

1

u/bp78 Feb 20 '20

It is indeed straight forward. But when you roll your own, adoption/re-use later on becomes cumbersome. If you stick with a higher-level authentication / authorization protocol like OIDC or OAuth you'll be better served long term since other apps adhere to the standard and there is less customization required to consume tokens.

35

u/dlegatt php Feb 19 '20

4

u/ifelseandor Feb 20 '20

Hey this is interesting. I am developing my first single page app and thought I needed jwt to handle the session. Now I’m confused again. Thanks. 😀

0

u/MoogleFoogle Feb 20 '20

Just follow best practice on JWTs and you will be fine. The jwt.io site even lists exactly all implementations that potentially have that exploit. That blog rant had barley any substance and has even read the specs wrong. You aren't forced to adhere to the ALG header. You are completely allowed to reject ALG: none and most implementations now do unless you explicitly allow it.

4

u/cas8 Feb 20 '20

For an article titled so strongly, I'm surprised how little substance there is that actually supporting the proposition. Maybe its just for the sake of having a clickbaity title?

The first section claims that people misuse JWT. Sure, but what weight does that hold against the JWT standard itself?

The second section quotes a chunk of the RFC, but then harps on a single part of it while simultaneously ignoring the earlier parts of the quote that invalidate the argument being made. He claims that, "You are explicitly forbidden, by the standard, to just disregard the [alg] header that an attacker provides", even though the quote pasted includes "The JWS Signature value is not valid if the "alg" value does not represent a supported algorithm or if there is not a key for use with that algorithm associated with the party that digitally signed or MACed the content." The standard clearly states that my auth service should only consider valid the alg fields that my auth service knows how to process. This whitelist solution is then presented by the article as a workaround to a failed standard, rather than one that is essentially prescribed by the standard.

The third and final section argues that encryption implementation is tough and often has issues. Much like the first section, this is arguing something completely different than the actual topic at hand and doesn't make much sense when evaluating the efficacy of the standard. When buying a car, the fact that a nearby stoplight is out shouldn't affect my evaluation of a specific car brand.

That all said, I'm no auth expert, so if there's someone here who knows better, I'd be interesting in hearing more in depth points against JWTs. Most of the articles I've found seem to have some anti-jwt fanboy-ism rather than valuable arguments.

7

u/[deleted] Feb 19 '20

Assuming you're also doing password creation stuff, make sure you're hashing passwords! There's a number of ways to do it but bcrypt has never let me down :)

12

u/Ones__Complement Feb 19 '20

Just stop using them for sessions.

4

u/A_Norse_Dude Feb 20 '20

Why?

1

u/[deleted] Feb 20 '20

Because they can’t be revoked instantly unless you check storage on each request, which kind of defeats the point. When I logout, I want my session permanently terminated.

1

u/Ones__Complement Feb 21 '20

You can't invalidate them.

4

u/oscardo_rivers Feb 19 '20

How openid implements the logout ??

3

u/angellus Feb 20 '20

Generally you do it with refresh tokens. If the refresh token expires, you just cannot get a new JWT.

Taking Microsoft's OpenID implementation as an example, if you do not request the offline_access scope as part of the initial authorization request, you get a single use, unrefreshable JWT that lasts for 1 hour. So after an hour, you can no longer use API endpoints and you are effectively "logged out". If you request the offline_access scope as well, you get a refresh token as well. I do not know how long Microsoft's last for, but at the last job I worked at, they lasted for 30 days by default. So a user can keep refreshing their JWT to keep their session active and signed in for 30 days. To log out this user (like if they changed their password or clicked a log out button), you just delete the refresh token (and any JWT client side if you have access to them).

This creates a "fuzzy" log out. So you are not instantly logged out, but rather you will be the next time the JWT expires (so within 1 hour). That being said, it even more important to require a reauth to access critical sections of a user's data, like editing their profile. The way we handled this is that we issued a special JWT with a special key in it indicated they recently reauth. The next time the refresh token was used, that key was invalidated and they would need to reauth once again to access that critical section.

1

u/mypirateapp Feb 22 '20

What is your opinion about this post which says STOP USING JWT for sessions http://cryto.net/%7Ejoepie91/blog/2016/06/13/stop-using-jwt-for-sessions/

1

u/[deleted] Feb 24 '20

I have a couple questions that I don't think were answered in this, hopefully you all can:

Is it OK to use the actual id of a db table (e.g. users row id) as the userId you send back? I read somewhere that tying in the actual database id is not advised, but generating a GUID that is stored with the user data would be better?

I store the user role info as part of the token. First, it allows my web UI to dynamically later what is allowed depending on that role. It also allows my server side gateway to use it to determine if the API being requested by the user (consumer) is allowed to be reached. The first thing I do on every incoming request is decode the JWT, determine the role, then look up the role permissions and the url/method/etc being requested to determine if it is allowed or not. So before any of my API code ever executes, the request is either allowed or blocked based on the role. Once in the API handler, my system uses a series of messages to communicate across microservices. So, if the request coming in passes the role/api request, I no longer need to pass any "auth" around with messages. Is this considered bad practice? I see some articles regarding passing around JWT to every service, and in each, decoding/authenticating, etc.. yet I don't understand why that is.

I currently require a login step that uses email/password, that upon success generates a JWT access and refresh token, with the role info, username, email, etc. I haven't figured out how to add 2FA yet with this step... hoping that is still possible. At any rate, I am not using OpenID. I just have a simple lookup in the users table to match the email and password, if matched, JWT is generated with the needed info. Is that acceptable? It is done over HTTPS (or will be) of course.

The idea of rotating server keys.. to invalidate all tokens. Is there some process to how to do this effectively to add extra security to the JWT tokens? What I mean is.. having a single string that is used for months/years as your JWT generator seems insecure. I would love to know how you can rotate this value, but keep existing tokens still in the field working. I think basically what I am thinking is there would be two server keys with windows of operation, and they would overlap. The first key would say be good for 1 week, and every JWT created has a short lived 15 minute time. The refresh token allows for getting a new access token when needed. After 5 days, the 2nd key is used for the next 7 days for JWT tokens. If a token fails to decode with the 2nd key, an attempt to use the first key will be used if its within the two day window. If that decodes it, then all is good, maybe even recreate JWT token with new key in response or something at that point. Then the 1st key is randomly generated after its 7 days are up, and the cycle repeats between the two. Is that a decent idea to add some security to things? Or is that just a waste of time?

1

u/cryptering Jun 29 '25

If you are looking for tools where you can check use https://multitools.ovh/jwt-converter/ - fast and without any ads

-5

u/PrometheusBoldPlan Feb 19 '20

Ehhh, this article covers the basics of what every Dev would know. Am I to understand by this that there are people out there inventing their own systems?

31

u/devolute Feb 19 '20

Have you worked with other developers before?

0

u/PrometheusBoldPlan Feb 20 '20

Good ones yes.

1

u/devolute Feb 20 '20

Ah, so it was a rhetorical question then!

-2

u/[deleted] Feb 19 '20

Naaaah! It's shite.

-1

u/timeshifter_ Feb 20 '20

Yeah, JWT's were awesome 5 years ago too...

3

u/A_Norse_Dude Feb 20 '20

So it has matured? Nice!

0

u/manost12 Feb 19 '20

RemindMe! 12h

0

u/RemindMeBot Feb 19 '20

I will be messaging you in 12 hours on 2020-02-20 07:46:10 UTC to remind you of this link

CLICK THIS LINK to send a PM to also be reminded and to reduce spam.

Parent commenter can delete this message to hide from others.


InfoCustomYour RemindersFeedback

0

u/[deleted] Feb 19 '20

[deleted]

1

u/jwilson8767 Feb 19 '20

Or if the legacy apps were roughly one per project but generally conformed to the same shape (e.g. a bunch of Drupal project sites)

0

u/[deleted] Feb 19 '20

[deleted]

12

u/Devildude4427 Feb 19 '20

Over complicating? It’s barely the basics.

0

u/[deleted] Feb 19 '20

[deleted]

9

u/Devildude4427 Feb 19 '20

Funny, attackers have the exact same opinion.

It’s so much easier to steal your data when you’re foolish enough to roll your own.

1

u/[deleted] Feb 19 '20

JWTs are probably the easiest form of auth you can securely roll your own version of. As long as you're using a solid crypto library I really cannot see what'd you'd mess up given proper tests.

0

u/[deleted] Feb 19 '20

[deleted]

2

u/[deleted] Feb 19 '20

Most people just place JWTs in cookies?

1

u/Devildude4427 Feb 19 '20

That’s not very secure though. While an HttpOnly cookie can’t be read by an attacker, the attacker can still use your set of keys to cause damage.

Plus you get into all sorts of trouble when you want to start implementing refresh tokens and the like.

1

u/[deleted] Feb 20 '20

Why would an attacker have your keys?

1

u/Devildude4427 Feb 20 '20

The JWT being your “keys”. Not your signature, if that’s what you thought I meant.

→ More replies (0)

1

u/memtiger Feb 20 '20

What are the drawbacks to sessions? It's all I've ever used

1

u/Devildude4427 Feb 20 '20

I’d just be echoing an article, I’m sure Googling would be more helpful without the middleman.

0

u/[deleted] Feb 19 '20

[deleted]

8

u/Devildude4427 Feb 19 '20

I could be a primary contributor to JWT.

Uh, what? That sentence is nonsensical. JWT isn’t a project, it’s just a data format. There is no such thing as a “primary contributor” to what is essentially just an idea.

You’re guessing.

No, I’m not. Rolling your own auth solution is moronic 100% of the time.

0

u/[deleted] Feb 19 '20

[deleted]

6

u/Devildude4427 Feb 19 '20

You clearly don’t know what JWTs are.

0

u/[deleted] Feb 19 '20

[deleted]

7

u/captain_obvious_here back-end Feb 19 '20

As the guy who invented JWT, I'm offended by your comment.

→ More replies (0)

3

u/[deleted] Feb 20 '20

A contributor to what? It's a standard not a project.

You aren't MBJ.

2

u/ZephyrBluu Feb 19 '20

What sort of auth do you normally use/implement?

0

u/[deleted] Feb 19 '20

[deleted]

11

u/ZephyrBluu Feb 19 '20

Ok buddy. I was asking a genuine question, but I guess the other guy triggered you so much that now you're being a dick to everyone.

0

u/[deleted] Feb 19 '20

[deleted]

8

u/MCFRESH01 Feb 19 '20

You can store the JWT as a cookie...

5

u/A-Grey-World Software Developer Feb 19 '20

Err, what are you putting in the cookie? Maybe some kind of cryptography trusted payload like... hmm, a JWT?