Technical tutorial

Understanding and Creating JWTs

Learn what JWTs are, how JWT authentication works, and how to create JSON Web Tokens in Express with Node.js. Includes examples, use cases, and refresh tokens.

JWT (JSON Web Token): a compact, stateless way to handle authentication. JWTs replace traditional session-based authentication for scalability. Anatomy of JWT: Header (algorithm), Payload (claims), Signature (verification). Pros: scalable, stateless, cross-service portability.

Cons: harder to revoke early, requires careful handling. Tutorial: Create JWTs in Express/Node.js and test in Postman . Refresh tokens extend session security by issuing new access tokens. JWT stands for JSON Web Token , though most people just pronounce it ‘ jot .’

JWTs offer a fantastic alternative to standard session-based authentication cookie methods by providing all the information an application needs to authenticate and authorize a user’s session in a handy Base64 encoded stateless token that is stored completely on the client side. In this article, we’re going to break down:

Session- based Authentication and it’s pros and cons Why JWT- based Authentication is a great alternative to Session-based The anatomy of JWTs and how they’re encoded How to create your own JWTs in ExpressJS and test them out in Postman

To put it simply, JWTs are compact, stateless tokens used to authenticate and authorize a user’s requests in an application or website. Once encoded and signed, a JWT has three parts, each separated by a period: The Header contains an algorithm designated to encode and decode the token.

The Payload is composed of key/value pairs called “claims” that contain all the auth information (ie user ID, roles/permissions, expiration, etc.).

The Signature is the way the server verifies that the user has not tampered with the token, if the signature changes upon request, the server rejects the request and the sender is not authorized.

*Note: While JWTs are Bas64 encoded, they can be decoded. A JWT’s signature prevents tampering but does not hide its contents. Do not put secret information in the payload or header elements of a JWT (such as a password) unless it is encrypted. Stateless, fast, portable, stored on client’s browser.

Easy to scale across multiple servers and microservices. Once a token has been issued, it’s difficult to stop the session prior to its expiration. Before diving into JWTs, let’s clear up a common mix-up:

Authentication answers “Who are you?” . The server uses your credentials to prove that you are, in fact, who you say you are. Like logging in with the correct username and password.

Authorization answers “What are you allowed to do?” . Now that you’re logged in, which resources do you actually have access to? For example, are you just a user, or are you allowed admin access?

Session-based authentication is fine for small-scale applications but becomes more and more complex with multiple servers or microservices. Since session data is stored on the server, every server needs access to a single-source of truth in order to validate active sessions. Without the servers being in sync, discrepancies can occur, which leads to unrecognized sessions and failed user requests.

Session based storage solves this problem by allowing us to save sessions to a database or a centralized session store which holds the session information in one place so all the servers can access it. However, this adds a layer of complexity because more databases = more overhead.

With all this in mind, JWTs were created to mitigate many of the challenges that come with scaling large applications in a session based cookie system.

Now that we understand Session-Based Authentication, let’s take a look at how JWTs streamline session management by taking a look at the way a JWT Authentication Flow works: A JWT is a string with three sections separated by periods (.): This contains metadata about the token (ie: type = JWT, algorithm = HS256 or RS256).

This is made up of key/value pairs called “claims” which contain metadata about the user or the token itself like user ID, role, expiration, and any other data you may want the JWT to hold on to. Claims can be custom (private), registered, or public.

In an effort to avoid this wherever possible, the Internet Assigned Numbers Authority (IANA) maintains a registry of standard claim names. Some examples of Registered Claims include iss (issuer), sub (subject), and exp (expiration time). You can view the full list of registered claims at iana.org .

This is what makes JWTs so fantastic! It’s created by hashing the header and payload with a secret or private key . If anyone tampers with the data on the JWT, the signature no longer matches, and the session automatically expires which will require the user to log in again.

The signature makes sure the JWT hasn’t been tampered with.

It’s created by combining the header, payload, and a secret or private key through the algorithm specified in the header (like HS256 or RS256). The result is base64 encoded and makes up the third part of the JWT. If anyone alters the header or payload, the signature will no longer match, and the JWT becomes invalid.

The secret or private key should be kept safe in either a .env file during development or a secure environment variable provided by production infrastructure (like AWS Secrets Manager). You can create a random secret or private key by using the “crypto” library and invoking crypto.randomBytes(32).toString('hex'). This will give you a random, 64 character string.

Single Sign-On (SSO): A user can authenticate just once and then use that same token to access multiple independent applications or services. That’s why JWTs are a common choice in OAuth2 and OpenID Connect systems. Like when you sign into an application through your Google account!

Note: If you’re having a hard time picturing where the Authorization header is in an HTTP request, check out our article on the JavaScript fetch API .

Mobile Apps & SPAs: In browser based applications, cookies are stored on the browser, in mobile apps they’re not (unless the app uses WebView which does have its own cookie storage). That makes cookie-based sessions difficult to manage. Instead, the JWT is usually stored in secure storage and gets sent as a Bearer token in every HTTP request that requires authentication.

JWTs support a variety of use cases, including login authorization and secure API communication. For the purposes of this tutorial, the focus will be limited to the login flow using Express and Node.js , then we will test the setup with Postman. Open a new request tab and set the method to POST.

Enter the URL of your login route, for example http://localhost:3000/login .

In the Body tab, select raw JSON and send your “user’s credentials” (in this case just a username since this tutorial won’t be handling authentication). Send the request and look at the response in the body tab. Notice a message of “Logged in!”.

Click the “Cookies” tab to find the JWT we sent in our response! We will use this to access our “user profile” information (username and hairColor) in a moment.

We can either copy from the Cookies tab in the response section as seen above, or from the cookies tab in our request section as seen below.

Our JWT, it will be sent with every request from the browser in the form of a cookie. But, remember not all apps send JWTs this way. Instead, we can send it as a Bearer token by adding it as an Authorization header. Now that we have our JWT copied, navigate to the Authorization tab.

Now, paste it in as a Bearer token in the Authorization header. Switch the request to ‘get’ and change the url to http://localhost:3000/profiles . And now in the response body, we see our user’s profile information as sent from the server!

We couldn’t write an article about JWTs without mentioning refresh tokens . As we explained, you can configure the expiration for your JWT ( access token ). But, remember, if someone has access to your JWT, they can access your information on the server. This is where refresh tokens come in.

Refresh tokens can be created the same way as any other JWT and stored centrally (in a database for example). Since they are used far less frequently than access tokens, it’s still a more efficient process than session-based management in terms of storage. You can send it with every request to a server, either as a Bearer token in the header or in an httpOnly cookie.

JWTs scale better across large, distributed systems and avoid centralized session stores. If an application uses several servers, instead of storing your session information in a database, they can use JWTs which are stored on the client and contain all the information needed for authorization.

Not exactly. API tokens are a string of characters you can include in your API request. In this case, the server would have a database of valid tokens. If your token matches one in the database, the request succeeds. JWT is a format often used for API tokens, but not all API tokens are JWTs.

Base64 encoding can easily be decoded by anyone with access to the internet. For that reason, you should never add sensitive data to any part of your JWT.

JWTs can only be revoked if the token expires or the server registers that the signature has been tampered with in the process of verifying a user’s response. It is difficult to stop a JWT once it is in motion, so it is best practice to implement the use of refresh tokens and to keep expirations limited to minutes or at most a few hours.