pwshub.com

JWT authentication: Best practices and when to use it

Editor’s note: This JWT authentication tutorial was last updated on 12 September 2024 by Chigozie Oduah to discuss modern JWT inefficiencies, such as lack of encryption, reliance on JavaScript for token storage, vulnerabilities like XSS and CSRF attacks, and size constraints imposed by browsers.

JWT Authentication: Best Practices And When To Use It

In web development, authentication is one of the most complex aspects to implement yourself. Many web applications delegate authentication to third-party authentication services like Auth0 or rely on authentication built into the frameworks or tools they are built with.

This also means that many developers (maybe you too 🙂 ) don’t know how to build at least moderately secure authentication into their web applications. JWT provides an easy way to to do this

With knowledge of some of the security concerns to consider when using JWT, you can implement a more secure authentication as you see with third-party authentication services. So, in this guide, we’ll begin by covering what JWTs are, then we’ll go into how they’re used and why, and finally, we’ll go into the issues and concerns to look out for when using JWTs.

What is JWT?

JSON Web Token (JWT) is a standard for structuring data to be transmitted between two parties (commonly server and client). A JWT is a single string made up of two components, a JSON Object Signing and Encryption (JOSE) header and its claims (or payload), both base64url encoded and separated by a period (.).

This is the structure of a token:

(Header).(Payload)

Here’s an example of a token:

eyJhbGciOiJub25l4oCdfQ.ewogICJpZCI6ICIxMjM0NTY3ODkwIiwKICAibmFtZSI6ICJKb2huIERvZSIsCiAgImFnZSI6IDM2Cn0K

This token is constructed with these two components:

  • JOSE header:
    {
      "alg": "none"
    }
    // base64url encoded to: eyJhbGciOiJub25l4oCdfQ
    
  • Claims:
    {
      "id": "1234567890",
      "name": "John Doe",
      "age": 36
    }
    // base64url encoded to: ewogICJpZCI6ICIxMjM0NTY3ODkwIiwKICAibmFtZSI6ICJKb2huIERvZSIsCiAgImFnZSI6IDM2Cn0
    

The JOSE header contains details about the type of encryption, signing, or both applied to the token. "alg": "none” specifies that the token isn’t encrypted or signed.

Claims are the information that JWTs carry. In the context of user authentication and authorization, you can think of it as claims about a user. The claims in this token are made up of three fields id, name, and age.

JSON Web Tokens aren’t sent directly as JSON strings because they’re UTF-8 encoded. This means that they can contain characters that aren’t URL-safe (characters like “/” or “&” for example). They can’t be put safely in HTTP Authorization headers and URI query parameters.

To make tokens URL-safe, they’re encoded into base64url format. This allows them to be safely put in query parameters and authorization headers.

However, this form of JSON Web Tokens is unsecured because there’s no way of ensuring the integrity of its claims, making it very unsafe to use in user authentication.

So, How are JWTs used in authentication?

The type of JWTs used in handling user authentications are signed tokens (or JSON Web Signatures, JWSs). Signed tokens are essentially JWTs with a cryptographically generated signature, to ensure that the claims in the tokens haven’t been tampered with.

Three components go into making Signed tokens:

  • JOSE header — Information about the algorithm used to sign the JWT
  • Payload (claims) — A payload is a JSON Web Token that holds the data to carry.
  • Signature — This is a string of characters created by hashing the payload and header (or just the payload) using the algorithm specified in the JOSE header. After generation, the signature is base64url encoded and added to the JWS

This is the structure of a signed token:

(Header).(Payload).(Signature)

And here’s an example of a Signed:

ewogICJ0eXAiOiJKV1QiLAogICJhbGciOiJIUzI1NuKAnQp9.ewogICJpZCI6ICIxMjM0NTY3ODkwIiwKICAibmFtZSI6ICJKb2huIERvZSIsCiAgImFnZSI6IDM2Cn0K.f5zWM0sR0rCxPFEbLcid2DdD2rnQ3PkRtEy2rVkRXF0

The token is constructed from these components:

  • Header:
    {
      "typ":"JWT",
      "alg":"HS256"
    }
    // base64url encoded to: ewogICJ0eXAiOiJKV1QiLAogICJhbGciOiJIUzI1NuKAnQp9
    
  • Payload:
    {
      "id": "1234567890",
      "name": "John Doe",
      "age": 36
    }
    // base64url encoded to: ewogICJpZCI6ICIxMjM0NTY3ODkwIiwKICAibmFtZSI6ICJKb2huIERvZSIsCiAgImFnZSI6IDM2Cn0K
    
  • Signature: Generated by applying HMAC SHA-256 to the JWT: ewogICJ0eXAiOiJKV1QiLAogICJhbGciOiJIUzI1NuKAnQp9.ewogICJpZCI6ICIxMjM0NTY3ODkwIiwKICAibmFtZSI6ICJKb2huIERvZSIsCiAgImFnZSI6IDM2Cn0K ((Header).(Payload)), with a secret key value your-256-bit-secret.

So how are signed tokens used in authentication? Here’s a simplified outline of the process:

  1. A user signs into their account on an authentication server
  2. The authentication server returns a signed token with their account information or an ID (or both)
  3. The signed token is stored in the browser’s localStorage or sessionStorage or anywhere the website prefers to store it
  4. The signed token is retrieved and used anytime a part of the website needs authenticated access

Here’s a visual representation of the process:

Diagram showing the JWT authentication process between the user's browser, authentication server, and web application.

Now that you know how to use JWTs in authentication, the only question left is “Why?” Let’s look into that in the next section.

The problem JWT aims to solve

It turns out that authentication isn’t easy to implement securely. I’ve made many web projects with simple hand-written authentication processes, where I just store the user’s identifier and password as plain JSON strings in JavaScript localStorage and pass them to any region of my application that needs authenticated access.

Fortunately, those projects didn’t have many users (or any in most cases), so it wasn’t rewarding to exploit. If the web applications had many (and important) users and had authentication implemented this way, it would’ve meant disaster.

Signed tokens prevent these kinds of disasters by:

  • Removing the need to store passwords in localStorage: A session ID in a signed token is enough to identify users. If the signature is generated using the HMAC SHA-256 algorithm, and the key used to create the signature is kept with extreme secrecy, and as random as possible, you can rest assured that only the authentication server can produce and verify the signature (provided that an attacker doesn’t have access to a quantum computer, and know how to use it)
  • Removing the need for redundant database querying: If claims about a user can be stored in a JWT and the integrity of the claims can be assured with the signature in a JWS, an API can use those claims without raising any concerns

But JWTs aren’t perfect solutions for secure authentication. They still have issues and concerns to look out for (and possibly work around) when using them in your project.

What are issues and concerns to look out for?

JWTs like many other tools in the world, aren’t perfect. They’re good for user authentication, but not without shortcomings. In this section, I’ll address some popular concerns.

So let’s start with the first concern.

JWTs aren’t encrypted

Signed tokens provide the benefit of verifying the integrity of the claims in the tokens. This allows them to be useful for authentication purposes. This doesn’t mean that the claims stored in the tokens aren’t hidden.

If your web application needs to store sensitive information in tokens, the website needs to handle them with caution. Generally, you should avoid storing sensitive information in tokens because it is very difficult to protect them against all possible cybersecurity attacks.


More great articles from LogRocket:

  • Don't miss a moment with The Replay, a curated newsletter from LogRocket
  • Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
  • Use React's useEffect to optimize your application's performance
  • Switch between multiple versions of Node
  • Discover how to use the React children prop with TypeScript
  • Explore creating a custom mouse cursor with CSS
  • Advisory boards aren’t just for executives. Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

In cases where a web application needs to store sensitive information in tokens, encrypted forms of JWTs exist for this reason.

JWTs may need JavaScript to work

Compared to the internet of the early 2000s modern-day internet is more secure. But, on its own, the modern-day internet still isn’t a hundred percent secure. Anything that JavaScript has access to can still potentially be exploited.

Because of the structure of modern applications, it has become more important for JavaScript to have access to the tokens to, for example, send requests to APIs. However, web applications have reasons for their structure, and in some cases, JavaScript having access to the tokens is unavoidable. Fortunately, the internet has gotten secure enough for access to JavaScript to be less of a concern than it was in the earlier internet.

There isn’t a good solution to this concern. Regardless of where you store tokens, you’re opening the tokens to at least one form of exploit. Storing in cookies or sessions is open to CSRF (Cross-Site Request Forgery) attacks, and storing anywhere JavaScript can access is open to XSS (Cross-Site Scripting) attacks.

JWTs are subject to size constraints

Depending on how you store and transmit JWTs, they’re subject to size constraints imposed by browsers. For example, all browsers impose a 4 KB and 5 MB limit on the total amount of data that a web application can store in cookies and JavaScript localStorage respectively.

If your web application uses significant portions of these storage mechanisms (although unlikely), you can use session tokens instead. They’re smaller, but they can’t have payloads, with extra pieces of information, like with JWTs.

Next steps

JWTs are useful tools in user authorization and authentication, but they’re just standards. They’re not built directly into programming languages or many frameworks. Using them in many cases is based on how you (or the library you choose to generate and handle them) implement JWTs. If you want to learn how to implement them, you can check out our guide on implementing JWT authentication with Vue and Node.js.

Would you be interested in joining LogRocket's developer community?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now

Source: blog.logrocket.com

Related stories
6 days ago - Auth.js makes adding authentication to web apps easier and more secure. Let's discuss why you should use it in your projects. The post Auth.js adoption guide: Overview, examples, and alternatives appeared first on LogRocket Blog.
1 month ago - In this adoption guide, we’ll discuss some reasons to choose Fastify, key features, compare Fastify to some popular alternatives, and more. The post Fastify adoption guide: Overview, examples, and alternatives appeared first on LogRocket...
2 weeks ago - Deploy a cloud-native Java Spring Boot microservice stack secured with Auth0 on Azure AKS using Terraform and Kubernetes.
1 month ago - Use Firebase Auth, Auth0 or AWS Cognito (Amplify) with your Supabase project, secure your users with SMS based MFA, and use send hooks.
6 days ago - Compare Auth.js and Lucia Auth for Next.js authentication, exploring their features, session management differences, and design paradigms. The post Lucia Auth: An Auth.js alternative for Next.js authentication appeared first on LogRocket...
Other stories
1 hour ago - Ubuntu 24.10 ‘Oracular Oriole’ is released on October 13th, and as you’d expect from a new version of Ubuntu, it’s packed with new features. As a short-term release, Ubuntu 24.10 gets 9 months of ongoing updates, security patches, and...
3 hours ago - Did you know that CSS can play a significant role in web accessibility? While CSS primarily handles the visual presentation of a webpage, when you use it properly it can enhance the user’s experience and improve accessibility. In this...
4 hours ago - Design thinking workshops are your key to turning big problems into clear solutions. In this blog, I share how to run them efficiently and keep your team aligned. The post How to run a design thinking workshop appeared first on LogRocket...
4 hours ago - New memory-optimized X8g instances offer up to 3 TiB DDR5 memory, 192 vCPUs, and 50 Gbps network bandwidth, designed for memory-intensive workloads like databases, analytics, and caching with unparalleled price/performance and efficiency.
4 hours ago - Gain indispensable data engineering expertise through a hands-on specialization by DeepLearning.AI and AWS. This professional certificate covers ingestion, storage, querying, modeling, and more.