Editor’s note: This JWT authentication tutorial was last updated on 1 July 2021. It may still contain information that is out of date.
In this JWT authentication tutorial, you’ll learn when to use JWT, why you shouldn’t use JWT for sessions, and how to store JWTs in cookies to prevent security issues. We’ll also go over some general JWT best practices.
Here’s what we’ll cover:
- What is JWT?
- When to use JWT authentication
- Why you shouldn’t use JWTs as session tokens
- Using JWT for API authentication
- How to expire a single JWT token
- How to securely store JWTs in a cookie
- Using JWT for SPA authentication
- Using JWT to authorize operations across servers
- How to choose the best JWT library
JSON Web Tokens (JWT) is a JSON-encoded representation of a claim or claims that can be transferred between two parties.
Though it’s a very popular technology, JWT authentication comes with its share of controversy. Some say you should never use it. Others say JWT authentication is amazing.
The truth lies somewhere in between: the value of using JWT depends on your use case and project requirements.
Before we dig any deeper, let’s briefly review what JWT authentication is.
What is JWT?
A JWT is a mechanism to verify the owner of some JSON data. It’s an encoded, URL-safe string that can contain an unlimited amount of data (unlike a cookie) and is cryptographically signed.
When a server receives a JWT, it can guarantee the data it contains can be trusted because it’s signed by the source. No middleman can modify a JWT once it’s sent.
It’s important to note that a JWT guarantees data ownership but not encryption. The JSON data you store into a JWT can be seen by anyone that intercepts the token because it’s just serialized, not encrypted.
For this reason, it’s highly recommended to use HTTPS with JWTs (and HTTPS in general, by the way).
We’re not going to cover how JWTs are generated in detail. For an in-depth, up-to-date look at how JWT authentication works, check out “JWT authentication from scratch with Vue.js and Node.js.”
When to use JWT authentication
JWT is a particularly useful technology for API authentication and server-to-server authorization.
For a comprehensive guide on using JWT technology to authenticate APIs, check out “How to secure a REST API using JWT.”
Why you shouldn’t use JWTs as session tokens
On the other hand, you should not use JWTs as session tokens by default. For one thing, JWT has a wide range of features and a large scope, which increases the potential for mistakes, either by library authors or users.
Another issue is that you can’t remove a JWT at the end of a session because it’s self-contained and there’s no central authority to invalidate them.
Finally, to put it simply, JWTs are relatively large. When used with cookies, this adds up to a ton of overhead per request.
Using JWTs for session tokens might seem like a good idea at first because:
- You can store any kind of user details on the client
- The server can trust the client because the JWT is signed, and there is no need to call the database to retrieve the information you already stored in the JWT
- You don’t need to coordinate sessions in a centralized database when you get to the eventual problem of horizontal scaling
Ultimately, if you already have a database for your application, just use a sessions table and use regular sessions as provided by the server-side framework of choice.
Why? There is a cost involved in using JWTs: they are sent for every request to the server and it’s always a high cost compared to server-side sessions.
Also, while the security risks are minimized sending JWTs using HTTPS, there is always the possibility that it’s intercepted and the data deciphered, exposing your user’s data.
Using JWT for API authentication
A very common use for JWT — and perhaps the only good one — is as an API authentication mechanism.
JWT technology is so popular and widely used that Google uses it to let you authenticate to its APIs.
The idea is simple: you get a secret token from the service when you set up the API:
On the client side, you create the token (there are many libraries for this) using the secret token to sign it.
When you pass it as part of the API request, the server will know it’s that specific client because the request is signed with its unique identifier:
How to expire a single JWT token
How do you invalidate a single token? A no-effort solution is to change the server secret key, which invalidates all tokens. However, this is not ideal for users, who may have their tokens expired for no reason.
One way to do it is to add a property to your user object in the server database to reference the date and time at which the token was created.
A token automatically stores this value in the
iat property. Every time you check the token, you can compare its
iat value with the server-side
To invalidate the token, just update the server-side value. If
iat is older than this, you can reject the token.
Another way to achieve this is by establishing a blocklist in your database cached in memory (or, even better, an allowlist).
How to securely store JWTs in a cookie
A JWT needs to be stored in a safe place inside the user’s browser. If you store it inside localStorage, it’s accessible by any script inside your page. This is as bad as it sounds; an XSS attack could give an external attacker access to the token.
To reiterate, whatever you do, don’t store a JWT in local storage (or session storage). If any of the third-party scripts you include in your page is compromised, it can access all your users’ tokens.
Using JWT for SPA authentication
JWTs can be used as an authentication mechanism that does not require a database. The server can avoid using a database because the data store in the JWT sent to the client is safe.
Using JWT to authorize operations across servers
Say you have one server where you are logged in, SERVER1, which redirects you to another server SERVER2 to perform some kind of operation.
SERVER1 can issue you a JWT that authorizes you to SERVER2. Those two servers don’t need to share a session or anything to authenticate you. The token is perfect for this use case.
How to choose the best JWT library
How do you decide which JWT library to use in your project? A good place to start is this list of JWT libraries for token signing and verification.
Select your language of choice and pick the library that you prefer — ideally, the one with the highest number of green checks.
JWT is a very popular standard you can use to trust requests by using signatures, and exchange information between parties. Make sure you know when it’s best used, when it’s best to use something else, and how to prevent the most basic security issues.
Get setup with LogRocket's modern error tracking in minutes:
- Visit https://logrocket.com/signup/ to get an app ID.
- Install LogRocket via NPM or script tag.
LogRocket.init()must be called client-side, not server-side.
- (Optional) Install plugins for deeper integrations with your stack:
- Redux middleware
- ngrx middleware
- Vuex plugin
$ npm i --save logrocket
import LogRocket from 'logrocket';
Add to your HTML:
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
18 Replies to “JWT authentication: Best practices and when to use it”
I don’t understand some of the claims here.
“Don’t store it in local storage (or session storage). If any of the third-party scripts you include in your page gets compromised, it can access all your users’ tokens.”
Why does this matter, when you protect against CSRF with CSRF tokens?
I tried storing it in cookie httpOnly but my problem is I cannot pass as request authorization header when making a request to the backend. How will this be solved?
You don’t. Cookies are send with every request you make to the server, so you read from the cookie in the backend instead of the authorization header.
I am thinking to store in authorization the id from db that contains the token, in authorization header the id will be used, or just encrypt all tokens with your master password then add in header, then decrypt at some point :D, really nothing seems safe
How to send cookie while making api call. I tried to add cookie in the header but no use
While testing, superagent makes it easy to set token in cookies. I guess any http agent will help too
did you solve the problem coz i also had the same problem
After some research, yes. It’s automatically passed into the request cookies. Before I use req.headers.authorization in my middleware, now I have to use `req.cookies[‘name’]`. The idea of setting cookie as httpOnly is that you can never call it using JS to alter like localstorage.
״ there is always the possibility that it’s intercepted and the data deciphered״ – deciphered is not the right word here since JWT are serialised, not encrypted
“the possibility that it’s intercepted and the data deciphered, exposing your user’s data.”
We only store enough information to identify the user in the jwt token. It can be the user’s id, email, or even another access token (in case you want to implement remote logout or similar features). We don’t store sensitive data (e.g. password,…) in the token, so this should not be an issue.
“Using JWT to authorize operations across servers” do you have any examples for this?
As http is stateless, every request made is new to server, to solve this or remember user/request, people use sessions, where server sends session id, like php sends PHPSESSID(key of cookie) stored at client side in cookie. When user makes another request php server calculate that it’s not new user. Now what if your server redirects you to the another physical server let’s say from example.net to cdn.example.org having different task assigned to them. This can cause problem because only one of the server has the power/logic to decipher that sessid right? Now that can be solved with jwt since you need only need to copy secret_key or simply .env file. And you can still verify and compare passwords
I am new to JWT.
If not through JWT how should we send sensitive data (like a password) to a server while logging in.
Actually it is. If the backend gets id=1 as part of the JWT payload, then it will assume the request is made from the correctly authenticated user with id=1, and thus will complete any request made.
Sending a password (either for logging in, or for creating an account) has nothing to do with JWT. JWT is about authenticating to the server after you have already sent the password. To do this correctly you must only connect via HTTPS.
Hi there, nice article.
I still have a question: if JWT is stored in cookies (secured & httpOnly), then the application is vulnerable to CSRF attacks, am I right?
And if the JWT is stored elsewhere accessible from JS then, the app is vulnerable to XSS.
What is the best solution?
Good article. Thank you
Great article. Really helped me figure out my backend authentication strategy, thanks again LogRocket!
Recommendation: replace the terms “blacklist” and “whitelist” with “blockedlist” and “allowedlist”. I know they’re traditional terms but the racial undertones are not friendly and could be done away with 🙂