<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ JSON Web Tokens (JWT) - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ JSON Web Tokens (JWT) - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:24:42 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/json-web-tokens-jwt/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ The JSON Web Token Handbook: Learn to Use JWTs for Web Authentication ]]>
                </title>
                <description>
                    <![CDATA[ JWT stands for JSON Web Token, and it’s one of those terms you’ll constantly come across in modern web development. At its core, a JWT is a JSON-based open standard format that allows you to represent specific claims securely between two parties. The... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-json-web-token-handbook-learn-to-use-jwts-for-web-authentication/</link>
                <guid isPermaLink="false">68e6ab03a5598a61a63ce39d</guid>
                
                    <category>
                        <![CDATA[ JSON Web Tokens (JWT) ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JWT ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ token ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sumit Saha ]]>
                </dc:creator>
                <pubDate>Wed, 08 Oct 2025 18:18:43 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759947512495/9c8aee78-1a83-4958-8c01-110e2247286d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>JWT stands for JSON Web Token, and it’s one of those terms you’ll constantly come across in modern web development.</p>
<p>At its core, a JWT is a JSON-based open standard format that allows you to represent specific claims securely between two parties. The exciting part is how widely JWT is used, especially in microservice architectures and modern authentication systems.</p>
<p>In this article, we’ll break down what JWTs really are, explore their structure, and see exactly how they help secure web applications. By the end, you’ll understand why developers rely on JWTs every single day.</p>
<h2 id="heading-heres-what-well-cover">Here’s What We’ll Cover</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-jwt">What is a JWT?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-do-we-need-tokens">Why Do We Need Tokens?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-session-tokens-the-classic-approach">Session Tokens: The Classic Approach</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-jwt-the-modern-solution">JWT: The Modern Solution</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-jwt-structure-header-payload-amp-signature">JWT Structure: Header, Payload &amp; Signature</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-example-decoding-a-jwt">Example: Decoding a JWT</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-jwts-ensure-security-the-signature">How JWTs Ensure Security: The Signature</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-security-considerations-and-token-management">Security Considerations and Token Management</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-jwts-in-different-languages">How to Create JWTs in Different Languages</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-practical-implementation-jwt-authentication-with-express-mongodb">Practical Implementation: JWT Authentication with Express + MongoDB</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-project-setup-amp-dependencies">1. Project Setup &amp; Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-project-folder-structure">2. Project Folder Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-step-by-step-implementation">3. Step-by-Step Implementation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-how-to-test-your-api">4. How to Test Your API</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-words">Final Words</a></p>
</li>
</ol>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>To follow along and get the most out of this guide, you should have:</p>
<ol>
<li><p>Basic familiarity with JavaScript / Node.js</p>
</li>
<li><p>Node.js and npm installed on your local machine</p>
</li>
<li><p>Basic understanding of HTTP and REST APIs</p>
</li>
<li><p>Understanding of JSON and how to parse/serialize it</p>
</li>
<li><p>Basic knowledge of Express (or ability to follow along)</p>
</li>
<li><p>A running instance of MongoDB (local or remote)</p>
</li>
<li><p>Experience with asynchronous code / Promises / async-await</p>
</li>
<li><p>Familiarity with environment variables / .env setup</p>
</li>
</ol>
<p>I’ve also created a video to go along with this article. If you’re the type who likes to learn from video as well as text, you can check it out here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/6drpx_QcMdg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-what-is-a-jwt">What is a JWT?</h2>
<p>JWTs are most commonly used for authentication today, but that wasn’t actually their original purpose. They were created to provide a standard way for two parties to securely exchange information. In fact, there’s even an industry standard specification (<a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc7519">RFC 7519</a>) that lays out exactly how JWTs should be structured and how they’re meant to be used for data exchange. Think of it like <a target="_blank" href="https://en.wikipedia.org/wiki/ECMAScript#:~:text=ECMAScript%20\(%2F%CB%88%C9%9Bkm,pages%20across%20different%20web%20browsers.">ECMAScript</a>, or ES, which defines the standard for JavaScript.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525281325/62565bc2-dc09-4565-8e5b-12b6333e6ff6.jpeg" alt="Client Server Secure Communication" class="image--center mx-auto" width="1919" height="1080" loading="lazy"></p>
<p>In real-world applications, JWTs are primarily used for authentication, and that’s the angle we’ll focus on in this article.</p>
<p>But remember that JWTs weren’t designed only for authentication. There are other ways to handle authentication too, and one of the most popular alternatives is session tokens.</p>
<h2 id="heading-why-do-we-need-tokens">Why Do We Need Tokens?</h2>
<p>Whatever authentication strategy we use, whether it’s a session token or a JWT, the underlying reason is the same: the stateless nature of the HTTP protocol.</p>
<p>When we exchange requests and responses from a browser to a server or between servers using HTTP, the protocol itself does not retain any information.</p>
<p><em>Stateless</em> means that during interactions between the client and the server, HTTP doesn’t remember any previous requests or data. In other words, every request must carry all the necessary information separately. HTTP doesn’t store any data on its own. Once it receives information, it forgets it. That’s why we say HTTP is stateless, as it has no inherent state or persistent information.</p>
<p>Think of it this way: when we access a webpage from a server, what information do we actually send to the server? If it’s a simple static website, we don’t need to send much. We just send the URL of the page to the server, and the server responds by delivering the corresponding HTML page. This means the server doesn’t need to remember any information or maintain any state, which is exactly how HTTP is designed to work, because HTTP itself is stateless.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525352836/7e6081f5-7d34-462a-9a7d-bcffd0242e00.jpeg" alt="Simple HTML Response from a Static Website" class="image--center mx-auto" width="1919" height="1080" loading="lazy"></p>
<p>But if the web application provides different responses for each user – in other words, if the website is dynamic – then sending only the URL is not sufficient. The user must also send their identity along with the URL to the server.</p>
<p>For example, if a user wants to access <code>page-1</code>, they must tell the server: “<em>I am User A, please give me page-1.</em>” The server will then respond with <code>page-1</code> accordingly. But next time, if the user requests, “<em>Now give me page-2</em>”, what will the server do? Since HTTP is stateless, if the request doesn’t include the user’s identity, the server won’t know which response to provide. This means that with every request, the user must provide their identity, right?</p>
<p>But if we look at the websites around us, do we really have to provide our identity every single time? Take Facebook as an example. Once we authenticate and log in, the server shows us the homepage when we request it, or our profile page when we request that, without requiring us to authenticate with every single request.</p>
<p>So the question is, if HTTP is stateless, how is this possible? How does the web application remember our browsing session? The answer is that, web applications can maintain sessions in different ways, and one of the most common methods is by using <strong>tokens</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525399836/7b7cdeab-4baa-4cda-bbeb-aaf4e4d4170c.jpeg" alt="How Server Remember our Browsing Session?" class="image--center mx-auto" width="1918" height="1077" loading="lazy"></p>
<h3 id="heading-session-tokens-the-classic-approach">Session Tokens: The Classic Approach</h3>
<p>There are two popular options for this. One is a <strong>Session Token</strong>, and the other is a <strong>JSON Web Token (JWT)</strong>. Let’s understand both so that it becomes clear what JWTs are and why they’re used.</p>
<p>Imagine a scenario in a company’s customer care department. A customer calls in with a complaint. The customer support representative listens to the issue and tries various troubleshooting steps but is unable to resolve the problem.</p>
<p>At this point, they forward the case to their higher management team and create a case file for the customer. This file contains all conversations with the customer and details of the troubleshooting attempts. The customer is then given a case ID or ticket ID, so that the next time they call, they don’t have to go through the same steps all over again.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525453002/c56bb7da-f6dd-4afe-b16b-966149bc7f91.jpeg" alt="Customer Care Scenerio 1 - Session Token Analogy" class="image--center mx-auto" width="1920" height="1080" loading="lazy"></p>
<p>The next day, when the customer calls again, they give their ticket ID to the customer care representative. The representative searches the system using that ticket ID, retrieves the details, and is able to respond accurately to the customer.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525515798/426af5fa-ff38-4ce2-ae1b-48ca1a8f1e6c.jpeg" alt="Customer Care Scenerio 2 - Session Token Analogy" class="image--center mx-auto" width="1920" height="1080" loading="lazy"></p>
<p>This scenario illustrates how authentication works in a web application using a session token. When a user authenticates, the server creates a session and keeps track of it. A session ID is generated for that session and sent back to the user, similar to the support ticket in the earlier example. From then on, whenever the user sends a request to the server, they include this session ID or token. The server looks up the session using that ID and identifies the client. Since the server has to handle multiple clients, this session token method has become an effective and widely used strategy for authentication.</p>
<p>And how the client sends the session ID to the server can vary depending on the implementation. The most common method is to store the session ID in the browser’s cookies. The advantage of this approach is that whenever the browser sends a request to the same server, it automatically adds the cookie information to the request header. This is a built-in behaviour of browsers, so no extra steps are needed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525561275/5881de41-571d-40ca-a0f7-4022d8c41754.jpeg" alt="Session Token Example" class="image--center mx-auto" width="1920" height="1080" loading="lazy"></p>
<p>When the user authenticates, the server saves data in the browser’s cookie, and from then on, that cookie information is sent automatically with every request, allowing the server to recognize the user. This was a very popular method, although in modern applications it has become a bit outdated.</p>
<p>But this mechanism has some issues. The biggest problem is that it assumes there is only a single server. In modern web applications, there are usually multiple servers. In such cases, a load balancer sits in front and decides which server will handle the user’s request.</p>
<p>Let’s say the session token method is being used. When the user sends the first request, the load balancer forwards it to <code>Server-1</code>. <code>Server-1</code> creates a session ID and sends it back to the client. Later, when the user sends another request, the load balancer routes it to <code>Server-2</code>. But <code>Server-2</code> doesn’t have that session ID stored, so how will it know which user the request belongs to?</p>
<p>The common solution to this is to store session IDs not on a specific server but in a shared <a target="_blank" href="https://redis.io/">Redis</a> database, so that any server can verify the session ID from there. This is what’s called a <strong>Redis cache</strong>. But in a microservice architecture, this approach has a weakness. If for some reason the Redis cache goes down, the servers may still be running, but the authentication mechanism will fail. This is exactly where JSON Web Tokens come in, offering a slightly different approach.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525611999/a970e2d9-6663-4a4e-9c63-37ea13470b90.jpeg" alt="Session Token Handling Multiple Servers with Redis Cache" class="image--center mx-auto" width="1920" height="1080" loading="lazy"></p>
<h3 id="heading-jwt-the-modern-solution">JWT: The Modern Solution</h3>
<p>Let’s revisit the customer care department example. This time, imagine there’s no phone or system. The customer comes directly to the office and meets the support agent in person. Since the agent doesn’t have any system this time, they can’t store all the information like before. Instead, they write everything down on a piece of paper and tell the customer, “<em>Next time you come, bring this with you.</em>”</p>
<p>This means the method is a bit different from the previous concept, right? But there’s still a problem: “<strong>validity</strong>”. If the customer isn’t legitimate and acts maliciously, how can the support representative trust them? The next day, if the customer comes in with the same information written on a blank sheet of paper, how can the agent verify the validity of their identity?</p>
<p>In this case, a possible solution is for the customer care executive to sign the paper when giving it to the customer. Then, when the customer brings the paper back, the support representative can verify the signature and confidently provide the service.</p>
<p>JSON Web Tokens work in a similar way. Here, when the client authenticates, instead of the server saving all the information, it sends all the user’s information as a JSON token along with a signature. Later, with each subsequent request, the client sends the entire token along with the request, which contains information like which user it is, their name, and other necessary details.</p>
<p>In this case, the server doesn’t save anything, and all the information stays with the client. Each time the client sends a request with this token, the server can read it, identify which user made the request, and provide the necessary data.</p>
<p>This token is not just a simple ID. It’s a JSON object containing all the information, and this is what we call a JSON Web Token. How the client stores this JWT is entirely up to the client. The most common methods are storing it in the browser’s cookies or local storage.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525648690/691848c9-e4c2-4b3f-b3f5-06623627e38f.jpeg" alt="JSON Web Token Analogy" class="image--center mx-auto" width="1920" height="1080" loading="lazy"></p>
<h3 id="heading-jwt-structure-header-payload-amp-signature">JWT Structure: Header, Payload, &amp; Signature</h3>
<p>As mentioned, the server receives a JSON object, but a JWT doesn’t look like a regular JSON.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525702339/f74219b8-4a01-4ac4-920b-449faf103520.png" alt="JWT Structure" class="image--center mx-auto" width="1920" height="1078" loading="lazy"></p>
<p>In the image above, it may seem a bit unusual. In fact, it’s an encoded version of the JSON object, a kind of scrambled or compact representation. If you look closely, you’ll see that a JWT is divided into three parts, separated by dots. The first part is the <strong>header</strong>, the second part is the <strong>JSON payload,</strong> which essentially holds our data, and the third part is the <strong>signature</strong>.</p>
<p>If we examine each part individually:</p>
<ul>
<li><p>The <strong>header</strong> is a separate JSON object.</p>
</li>
<li><p>The <strong>payload</strong> is also a separate JSON object containing our data.</p>
</li>
<li><p>The third part is the <strong>signature</strong>.</p>
</li>
</ul>
<p>But what does the signature mean here? Simply put, the signature is a hash value. Our data is hashed using a secret key to create the signature. This secret key is kept on the server. So, when this JSON Web Token is sent to the server, the server can use that secret key to verify the signature. This ensures that the token is valid and has not been tampered with.</p>
<h2 id="heading-example-decoding-a-jwt">Example: Decoding a JWT</h2>
<p>Let’s look at an example. The best website for working with JWTs and understanding their structure is <a target="_blank" href="http://jwt.io">jwt.io</a><a target="_blank" href="https://jwt.io/">.</a> If you paste a JWT into the site, three sections appear: the header, payload, and signature. The payload is shown in the “Decoded Payload” section, which contains content and data. You’ll see there’s an ID, a JSON object with a name, and an expiration time.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525738886/84c2532f-dc09-4a96-83de-ecd4a24d958f.jpeg" alt="Decoding a JWT" class="image--center mx-auto" width="636" height="367" loading="lazy"></p>
<p>The header is also a completely valid JSON object, which specifies an algorithm and shows the type –essentially indicating which algorithm will be used to create or verify this JWT.</p>
<p>So, the main data is in the “Decoded Payload” section, and the third part is the signature. Now there’s an important point to note: you might wonder where this scrambled-looking token comes from. It’s actually very simple. The data in the “Decoded Payload” is <strong>Base64 encoded</strong>, and that’s what forms the appearance of this scrambled token.</p>
<p>If you copy this part of the JWT and paste it into any online Base64 decoder, you’ll immediately see the data.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525794705/4ee950a2-2ad0-40b4-8287-fdfea9543a6f.png" alt="Base64 Encode Decode" class="image--center mx-auto" width="1919" height="1080" loading="lazy"></p>
<p>What does this mean? It means that if this data is encoded again using Base64, the same token will be generated. The header works the same way as well.</p>
<p>And the final point: the scrambled or encoded part. Is it done for security? No, it’s not for security. It’s done purely for convenience. JSON objects can be quite large, and not all programming languages handle them in the same way. In JavaScript it’s easy, but in other languages, it can sometimes cause issues. So to make it easier to handle, the data is Base64 encoded. This is not for security, as encoding it like this doesn’t make the data secure, because the information can still be viewed publicly.</p>
<p>As you can see in the diagram above, the moment you enter it on this site, your data is immediately visible. This means that no sensitive information should be stored here, only user identification details, like a user ID or other public information. <strong>Passwords or any secret keys should never be stored in the token, because they can be easily read.</strong> Even though it looks scrambled or encoded, it is actually public.</p>
<h2 id="heading-how-jwts-ensure-security-the-signature">How JWTs Ensure Security: The Signature</h2>
<p>Now let’s move to the security part, which is ensured by the signature. In our earlier paper example, a person could simply add a signature by hand.</p>
<p>But for data, the process of creating a signature is different. For data, the signature is created cryptographically using a secret key, which is the actual signature. The process of creating the signature is as follows:</p>
<ol>
<li><p>The data is Base64 encoded.</p>
</li>
<li><p>It is concatenated with the secret key.</p>
</li>
<li><p>It is encoded again in Base64.</p>
</li>
</ol>
<p>The configuration specifies an algorithm. This algorithm can be changed, but the same algorithm used to create the token must be used to verify it. In other words, the algorithm for generating and verifying the token must always be the same.</p>
<p>Finally, the data is hashed using a secret key. This secret key is not available to the public. Instead, it’s kept only on the server, usually stored securely in a server vault. When this JWT reaches the server, the server uses the secret key to verify whether the token is valid. If it doesn’t match correctly, it will display “invalid signature.” This ensures that the server can confirm whether the token has been tampered with and that its integrity is intact.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525829955/bf017016-d9fd-43cb-836a-eafe4f35540b.jpeg" alt="The Big Formula" class="image--center mx-auto" width="1224" height="1078" loading="lazy"></p>
<p>For example, if you use <code>love-you-all-from-logicbaselabs</code> as the signature, and the server verifies it, it will show “<em>signature verified</em>”. This demonstrates that the secret key exists only on the server. This ensures that even though public information is displayed, the token’s validity can be confirmed.</p>
<p>JSON Web Tokens aren’t like a password, though. They primarily serve to identify the user. The server can check the JWT to determine whether it belongs to a valid user. In other words, the JWT represents the user’s identity. It’s a very important token, containing secure content along with the signature.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759525873387/a434b453-0a38-41a5-93f3-bd12b46806f3.jpeg" alt="Signature Verification" class="image--center mx-auto" width="1920" height="1078" loading="lazy"></p>
<h2 id="heading-security-considerations-and-token-management">Security Considerations and Token Management</h2>
<p>One important thing to remember: if someone gets hold of your JWT, meaning they have the exact same token, they can easily log in as that user. They just need to send requests with that token to gain the necessary access.</p>
<p>You could think of it like this: if someone gets hold of your Facebook password, they can log in to your Facebook account. Similarly, if someone obtains your PayPal account PIN, they can easily access your account. In other words, if someone gets hold of your most secure information, there’s no way to protect it.</p>
<p>The same applies to JWTs: keeping the token safely on the client side is absolutely crucial. In this regard, we are somewhat vulnerable.</p>
<p>There is, though, one key difference. In the case of session tokens, if we assume an account has been compromised, the server can invalidate that session. In other words, no one can log in using that session ID anymore.</p>
<p>But with a JWT, the token remains valid until its expiration time. So there’s no direct way to invalidate it. Since the token is cryptographically self-contained and signed with the server’s secret key, once it’s created, it cannot be directly revoked by the server.</p>
<p>The only way to handle this is what’s done on the web: denylisting the token. In other words, the server maintains a separate database listing all JWT tokens that are denylisted. Whenever a request comes in, the server first verifies whether the token is valid. Then, through middleware, it checks whether the token is on the denylist. Only if it’s not on that list is the user allowed access.</p>
<p>So, these are the rules for using JSON Web Tokens. JWTs can be used in any programming language, especially in the context of REST APIs. They are extremely popular and widely used in microservice architectures.</p>
<h2 id="heading-how-to-create-jwts-in-different-languages">How to Create JWTs in Different Languages</h2>
<p>How you create a JWT depends on the programming language you’re using. For example, in Node.js, there are specialized libraries available, like <a target="_blank" href="https://www.npmjs.com/package/jsonwebtoken">jsonwebtoken</a>, so it’s straightforward. And in PHP, there are easy-to-use options for creating JWTs as well. So, JWTs are a universal tool, not limited to any specific programming language. Many people think they’re only for JavaScript, but that’s not true.</p>
<p>And remember that JWTs aren’t just used for authentication purposes. You can use them to represent any kind of identity. For example, if you’re going to a concert, access could be granted using a JWT instead of a regular ticket. When your client uses that JWT, the gateway or server can read the token, provide access to the information, and verify it using the signature.</p>
<h2 id="heading-practical-implementation-jwt-authentication-with-express-mongodb">Practical Implementation: JWT Authentication with Express + MongoDB</h2>
<p>In this section, we will put into practice all the concepts we have learned so far. Using <a target="_blank" href="https://www.freecodecamp.org/news/the-express-handbook/"><strong>Express.js</strong></a> and <a target="_blank" href="https://www.freecodecamp.org/news/how-to-start-using-mongodb/"><strong>MongoDB</strong></a>, we will build a complete JWT authentication system step by step.</p>
<p>Don’t worry if it feels overwhelming at first. We will go carefully, one step at a time, and by the end, you will have a fully working project. Think of it as entering a building floor by floor: we’ll explore each section thoroughly and come out with a solid understanding.</p>
<h3 id="heading-1-project-setup-amp-dependencies">1. Project Setup &amp; Dependencies</h3>
<p>Before writing any code, we need to set up our Node.js project and install the required dependencies.</p>
<h4 id="heading-initialize-the-nodejs-project">Initialize the Node.js Project</h4>
<p>Open your terminal and run:</p>
<pre><code class="lang-javascript">mkdir jwt-auth-demo
cd jwt-auth-demo
npm init -y
</code></pre>
<p>This will create a <code>package.json</code> file with default settings.</p>
<h4 id="heading-install-dependencies">Install Dependencies</h4>
<p>We need some packages to build our JWT authentication system:</p>
<pre><code class="lang-javascript">npm install express mongoose bcryptjs jsonwebtoken dotenv
</code></pre>
<ul>
<li><p><code>express</code>: Fast and minimal Node.js web framework to create API routes.</p>
</li>
<li><p><code>mongoose</code>: ODM (Object Data Modeling) library to interact with MongoDB easily.</p>
</li>
<li><p><code>bcryptjs</code>: Library to hash and compare passwords securely.</p>
</li>
<li><p><code>jsonwebtoken</code>: Library to generate and verify JWT tokens.</p>
</li>
<li><p><code>dotenv</code>: Loads environment variables from a <code>.env</code> file to keep secrets secure.</p>
</li>
</ul>
<h4 id="heading-install-dev-dependencies-optional">Install Dev Dependencies (Optional)</h4>
<p>For development convenience, install <strong>nodemon</strong> to auto-restart the server on file changes:</p>
<pre><code class="lang-javascript">npm install --save-dev nodemon
</code></pre>
<p>Update <code>package.json</code> scripts:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"scripts"</span>: {
  <span class="hljs-string">"start"</span>: <span class="hljs-string">"node server.js"</span>,
  <span class="hljs-string">"dev"</span>: <span class="hljs-string">"nodemon server.js"</span>
}
</code></pre>
<ul>
<li><p><code>npm start</code> runs the server normally.</p>
</li>
<li><p><code>npm run dev</code> runs the server with auto-restart using <strong>nodemon</strong>.</p>
</li>
</ul>
<h3 id="heading-2-project-folder-structure">2. Project Folder Structure</h3>
<pre><code class="lang-javascript">jwt-auth-demo/
│
├── config/
│   └── db.js
│
├── controllers/
│   └── authController.js
│
├── middlewares/
│   └── authMiddleware.js
│
├── models/
│   └── User.js
│
├── routes/
│   └── auth.js
│
├── services/
│   ├── hashService.js
│   └── jwtService.js
│
├── .env
├── server.js
├── package.json
</code></pre>
<p><strong>What goes where?</strong></p>
<ul>
<li><p><code>config/</code>: Database connection and environment config.</p>
</li>
<li><p><code>controllers/</code>: Main logic for each endpoint.</p>
</li>
<li><p><code>middlewares/</code>: Functions that run before controllers (for example, auth checks).</p>
</li>
<li><p><code>models/</code>: Mongoose schemas.</p>
</li>
<li><p><code>routes/</code>: API endpoint definitions.</p>
</li>
<li><p><code>services/</code>: Reusable logic (hashing, JWT).</p>
</li>
<li><p><code>.env</code>: Secrets and config variables.</p>
</li>
<li><p><code>server.js</code>: Entry point of the app.</p>
</li>
</ul>
<h3 id="heading-3-step-by-step-implementation">3. Step-by-Step Implementation</h3>
<h4 id="heading-initialize-the-express-server">Initialize the Express Server</h4>
<p>Before doing anything complex, we need to set up a simple server using Express. Think of this as the heart of our application. This server will be responsible for listening to incoming requests (like user login or register) and sending back responses.</p>
<p><strong>File: server.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server.js</span>

<span class="hljs-comment">// Import the express library to build our server</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);

<span class="hljs-comment">// Create an instance of express</span>
<span class="hljs-keyword">const</span> app = express();

<span class="hljs-comment">// Middleware to parse JSON request bodies (important for APIs)</span>
app.use(express.json());

<span class="hljs-comment">// Default route to test server</span>
app.get(<span class="hljs-string">"/"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.send(<span class="hljs-string">"Hello World! Your server is working 🚀"</span>);
});

<span class="hljs-comment">// Start the server on port 5000</span>
<span class="hljs-keyword">const</span> PORT = process.env.PORT || <span class="hljs-number">5000</span>;
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<ul>
<li><p>We import Express and create an app instance.</p>
</li>
<li><p>We use middleware to parse JSON requests (important for APIs).</p>
</li>
<li><p>We define a simple route <code>/</code> to test if our server works.</p>
</li>
<li><p>We start the server on port 5000 and log a message when it's running.</p>
</li>
</ul>
<p>Now, let’s test it:</p>
<ul>
<li><p>Run <code>node server.js</code> or <code>npm run dev</code>.</p>
</li>
<li><p>Open your browser at <code>http://localhost:5000</code>.</p>
</li>
<li><p>You should see: <code>Hello World! Your server is working 🚀</code></p>
</li>
</ul>
<h4 id="heading-connect-mongodb-with-mongoose">Connect MongoDB with Mongoose</h4>
<p>In this step, we want to store users in a database. For that, we will use MongoDB. To interact with MongoDB in Node.js easily, we use Mongoose, which is an ODM library.</p>
<p><strong>File: config/db.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// config/db.js</span>

<span class="hljs-comment">// Import mongoose</span>
<span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);

<span class="hljs-comment">// Connect to MongoDB using environment variable</span>
<span class="hljs-keyword">const</span> connectDB = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">await</span> mongoose.connect(process.env.MONGO_URI, {
      <span class="hljs-attr">useNewUrlParser</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">useUnifiedTopology</span>: <span class="hljs-literal">true</span>,
    });
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"✅ MongoDB Connected"</span>);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"❌ MongoDB Connection Error:"</span>, err.message);
    process.exit(<span class="hljs-number">1</span>); <span class="hljs-comment">// Stop server if DB fails</span>
  }
};

<span class="hljs-built_in">module</span>.exports = connectDB;
</code></pre>
<p>Now our server is connected to MongoDB. Whenever we insert, update, or query data, it will go into this database.</p>
<p><strong>File: .env</strong></p>
<pre><code class="lang-javascript">PORT=<span class="hljs-number">5000</span>
MONGO_URI=mongodb:<span class="hljs-comment">//127.0.0.1:27017/jwt-auth-demo</span>
JWT_SECRET=your_super_secret_key
</code></pre>
<p>The .env file stores sensitive information like your database URI, JWT secret, and server port. By using environment variables, you can keep secrets out of your code and easily change configuration without modifying your source files. Never commit .env to public repositories to protect your credentials.</p>
<h4 id="heading-create-user-model">Create User Model</h4>
<p>In this step, we need to define how a User looks in our database. Each user will have a <strong>name, email, and password</strong>.</p>
<p><strong>File: models/User.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// models/User.js</span>
<span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);

<span class="hljs-comment">// Define a schema (blueprint of user data)</span>
<span class="hljs-keyword">const</span> userSchema = <span class="hljs-keyword">new</span> mongoose.Schema({
  <span class="hljs-attr">name</span>: { <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span> },
  <span class="hljs-attr">email</span>: { <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">unique</span>: <span class="hljs-literal">true</span> },
  <span class="hljs-attr">password</span>: { <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>, <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span> },
});

<span class="hljs-comment">// Create and export the model</span>
<span class="hljs-built_in">module</span>.exports = mongoose.model(<span class="hljs-string">"User"</span>, userSchema);
</code></pre>
<p>As you can see, each user now has a name, email, and hashed password. This ensures that every user we save has these three fields.</p>
<h4 id="heading-hashing-amp-jwt-services">Hashing &amp; JWT Services</h4>
<p>In this step, we will handle password hashing and JWT management using separate services. This keeps our code organized and reusable.</p>
<p><strong>File: services/hashService.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">//services/hashService.js</span>

<span class="hljs-keyword">const</span> bcrypt = <span class="hljs-built_in">require</span>(<span class="hljs-string">"bcryptjs"</span>);

<span class="hljs-comment">// Function to hash a plain password</span>
<span class="hljs-built_in">exports</span>.hashPassword = <span class="hljs-keyword">async</span> (plainPassword) =&gt; {
  <span class="hljs-comment">// bcrypt.hash generates a hashed version of the password</span>
  <span class="hljs-comment">// The number 10 is the salt rounds, which affects the hashing complexity</span>
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> bcrypt.hash(plainPassword, <span class="hljs-number">10</span>);
};

<span class="hljs-comment">// Function to compare a plain password with a hashed password</span>
<span class="hljs-built_in">exports</span>.comparePassword = <span class="hljs-keyword">async</span> (plainPassword, hashedPassword) =&gt; {
  <span class="hljs-comment">// bcrypt.compare checks if the plain password matches the hashed one</span>
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> bcrypt.compare(plainPassword, hashedPassword);
};
</code></pre>
<ul>
<li><p><code>hashPassword(plainPassword)</code>: Takes a plain text password and returns a hashed version using bcrypt. Never store plain passwords directly.</p>
</li>
<li><p><code>comparePassword(plainPassword, hashedPassword)</code>: Compares a user-entered password with the hashed password stored in the database. Returns <code>true</code> if they match.</p>
</li>
</ul>
<p><strong>File: services/jwtService.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// services/jwtService.js</span>

<span class="hljs-keyword">const</span> jwt = <span class="hljs-built_in">require</span>(<span class="hljs-string">"jsonwebtoken"</span>);

<span class="hljs-comment">// Function to generate a JWT</span>
<span class="hljs-built_in">exports</span>.generateToken = <span class="hljs-function">(<span class="hljs-params">payload</span>) =&gt;</span> {
  <span class="hljs-comment">// jwt.sign creates a signed token using our secret key from environment variables</span>
  <span class="hljs-comment">// expiresIn defines how long the token is valid (1 hour here)</span>
  <span class="hljs-keyword">return</span> jwt.sign(payload, process.env.JWT_SECRET, { <span class="hljs-attr">expiresIn</span>: <span class="hljs-string">"1h"</span> });
};

<span class="hljs-comment">// Function to verify a JWT</span>
<span class="hljs-built_in">exports</span>.verifyToken = <span class="hljs-function">(<span class="hljs-params">token</span>) =&gt;</span> {
  <span class="hljs-comment">// jwt.verify checks if the token is valid and not expired</span>
  <span class="hljs-keyword">return</span> jwt.verify(token, process.env.JWT_SECRET);
};
</code></pre>
<ul>
<li><p><code>generateToken(payload)</code>: Generates a JWT for a user. The <code>payload</code> typically contains user ID and email.</p>
</li>
<li><p><code>verifyToken(token)</code>: Verifies that the JWT is valid and returns the decoded payload if successful.</p>
</li>
<li><p>Using a separate JWT service keeps token logic centralized and easy to manage.</p>
</li>
</ul>
<h4 id="heading-auth-controller">Auth Controller</h4>
<p>In this step, we will handle all authentication-related logic in a separate controller. This keeps routes clean and separates business logic from endpoint definitions.</p>
<p><strong>File: controllers/authController.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// controllers/authController.js</span>

<span class="hljs-keyword">const</span> User = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../models/User"</span>);
<span class="hljs-keyword">const</span> { hashPassword, comparePassword } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../services/hashService"</span>);
<span class="hljs-keyword">const</span> { generateToken } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../services/jwtService"</span>);

<span class="hljs-comment">// Register new user</span>
<span class="hljs-built_in">exports</span>.register = <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { name, email, password } = req.body; <span class="hljs-comment">// Get user input</span>

    <span class="hljs-comment">// Step 1: Check if user already exists</span>
    <span class="hljs-keyword">const</span> existingUser = <span class="hljs-keyword">await</span> User.findOne({ email });
    <span class="hljs-keyword">if</span> (existingUser)
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"User already exists!"</span> });

    <span class="hljs-comment">// Step 2: Hash password using hashService</span>
    <span class="hljs-keyword">const</span> hashedPassword = <span class="hljs-keyword">await</span> hashPassword(password);

    <span class="hljs-comment">// Step 3: Save user to database</span>
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">new</span> User({ name, email, <span class="hljs-attr">password</span>: hashedPassword });
    <span class="hljs-keyword">await</span> user.save();

    <span class="hljs-comment">// Step 4: Send success response</span>
    res.status(<span class="hljs-number">201</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"User registered successfully!"</span> });
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-comment">// Handle errors gracefully</span>
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: err.message });
  }
};

<span class="hljs-comment">// Login user</span>
<span class="hljs-built_in">exports</span>.login = <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { email, password } = req.body; <span class="hljs-comment">// Get user input</span>

    <span class="hljs-comment">// Step 1: Find user by email</span>
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> User.findOne({ email });
    <span class="hljs-keyword">if</span> (!user)
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Invalid email or password"</span> });

    <span class="hljs-comment">// Step 2: Compare provided password with hashed password</span>
    <span class="hljs-keyword">const</span> isMatch = <span class="hljs-keyword">await</span> comparePassword(password, user.password);
    <span class="hljs-keyword">if</span> (!isMatch)
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Invalid email or password"</span> });

    <span class="hljs-comment">// Step 3: Generate JWT using jwtService</span>
    <span class="hljs-keyword">const</span> token = generateToken({ <span class="hljs-attr">id</span>: user._id, <span class="hljs-attr">email</span>: user.email });

    <span class="hljs-comment">// Step 4: Send success response with token</span>
    res.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Login successful!"</span>, token });
  } <span class="hljs-keyword">catch</span> (err) {
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: err.message });
  }
};

<span class="hljs-comment">// Protected profile route</span>
<span class="hljs-built_in">exports</span>.profile = <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-comment">// req.user is set by auth middleware after token verification</span>
  res.json({
    <span class="hljs-attr">message</span>: <span class="hljs-string">"Welcome to your profile!"</span>,
    <span class="hljs-attr">user</span>: req.user,
  });
};
</code></pre>
<ul>
<li><p><strong>File:</strong> <code>controllers/authController.js</code> – Contains all logic related to authentication.</p>
</li>
<li><p><code>exports.register</code> handles user registration:</p>
<ul>
<li><p>Checks if the user exists.</p>
</li>
<li><p>Hashes the password using <code>hashService</code>.</p>
</li>
<li><p>Saves the new user to MongoDB.</p>
</li>
<li><p>Returns a success message.</p>
</li>
</ul>
</li>
<li><p><code>exports.login</code> handles user login:</p>
<ul>
<li><p>Finds the user by email.</p>
</li>
<li><p>Compares passwords using <code>hashService.comparePassword</code>.</p>
</li>
<li><p>Generates a JWT token if valid.</p>
</li>
<li><p>Returns the token in the response.</p>
</li>
</ul>
</li>
<li><p><code>exports.profile</code> handles protected profile route:</p>
<ul>
<li>Returns user information from <code>req.user</code>, which is set by the auth middleware.</li>
</ul>
</li>
<li><p>Using a controller keeps route definitions clean and separates business logic from endpoint handling.</p>
</li>
</ul>
<h4 id="heading-auth-middleware">Auth Middleware</h4>
<p>In this step, we create a middleware to protect routes by verifying JWTs. Only authenticated users can access protected endpoints.</p>
<p><strong>File: middlewares/authMiddleware.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// middlewares/authMiddleware.js</span>

<span class="hljs-keyword">const</span> { verifyToken } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../services/jwtService"</span>);

<span class="hljs-comment">// Middleware to protect routes</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
  <span class="hljs-comment">// Step 1: Get Authorization header</span>
  <span class="hljs-keyword">const</span> authHeader = req.headers[<span class="hljs-string">"authorization"</span>];
  <span class="hljs-keyword">if</span> (!authHeader)
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">401</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"No token provided"</span> });

  <span class="hljs-comment">// Step 2: Extract token from format 'Bearer &lt;token&gt;'</span>
  <span class="hljs-keyword">const</span> token = authHeader.split(<span class="hljs-string">" "</span>)[<span class="hljs-number">1</span>];
  <span class="hljs-keyword">if</span> (!token) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">401</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Malformed token"</span> });

  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// Step 3: Verify token using jwtService</span>
    <span class="hljs-keyword">const</span> decoded = verifyToken(token);

    <span class="hljs-comment">// Step 4: Attach decoded user info to request object</span>
    req.user = decoded;

    <span class="hljs-comment">// Proceed to next middleware or route handler</span>
    next();
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-comment">// If token is invalid or expired</span>
    res.status(<span class="hljs-number">401</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Invalid or expired token"</span> });
  }
};
</code></pre>
<ul>
<li><p><strong>File:</strong> <code>middlewares/authMiddleware.js</code> – Middleware for protecting routes.</p>
</li>
<li><p>Step 1: Checks if the <code>Authorization</code> header is present.</p>
</li>
<li><p>Step 2: Extracts the token from the <code>Bearer &lt;token&gt;</code> format.</p>
</li>
<li><p>Step 3: Verifies the token using <code>jwtService.verifyToken</code>.</p>
</li>
<li><p>Step 4: Attaches the decoded user info to <code>req.user</code> for use in subsequent route handlers.</p>
</li>
<li><p>If the token is missing, malformed, invalid, or expired, the middleware responds with <strong>401 Unauthorized</strong>. This ensures only authenticated users can access protected routes.</p>
</li>
</ul>
<h4 id="heading-auth-routes">Auth Routes</h4>
<p>In this step, we will define authentication-related routes and connect them with the controller and middleware.</p>
<p><strong>File: routes/auth.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// routes/auth.js</span>

<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> router = express.Router();
<span class="hljs-keyword">const</span> authController = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../controllers/authController"</span>);
<span class="hljs-keyword">const</span> authMiddleware = <span class="hljs-built_in">require</span>(<span class="hljs-string">"../middlewares/authMiddleware"</span>);

<span class="hljs-comment">// Step 1: Register route</span>
<span class="hljs-comment">// Users send their name, email, and password to this endpoint</span>
router.post(<span class="hljs-string">"/register"</span>, authController.register);

<span class="hljs-comment">// Step 2: Login route</span>
<span class="hljs-comment">// Users send email and password to receive JWT</span>
router.post(<span class="hljs-string">"/login"</span>, authController.login);

<span class="hljs-comment">// Step 3: Protected profile route</span>
<span class="hljs-comment">// Only accessible to authenticated users with a valid JWT</span>
router.get(<span class="hljs-string">"/profile"</span>, authMiddleware, authController.profile);

<span class="hljs-built_in">module</span>.exports = router;
</code></pre>
<ul>
<li><p><strong>File:</strong> <code>routes/auth.js</code> – Central file to define authentication endpoints.</p>
</li>
<li><p><code>router.post("/register", authController.register)</code>: Handles user registration.</p>
</li>
<li><p><code>router.post("/login", authController.login)</code>: Handles user login and token generation.</p>
</li>
<li><p><code>router.get("/profile", authMiddleware, authController.profile)</code>: Protected route, requires JWT. The <code>authMiddleware</code> ensures only authenticated users can access it.</p>
</li>
<li><p>Using routes with controllers and middleware keeps the application organized and professional.</p>
</li>
</ul>
<h4 id="heading-main-server-file">Main Server File</h4>
<p>This is the main entry point of our application. It sets up the server, connects to the database, and mounts all routes.</p>
<p><strong>File: server.js</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server.js</span>

<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config(); <span class="hljs-comment">// Step 1: Load environment variables from .env</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> connectDB = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./config/db"</span>);

<span class="hljs-keyword">const</span> app = express();

<span class="hljs-comment">// Step 2: Connect to MongoDB</span>
connectDB();

<span class="hljs-comment">// Step 3: Middleware to parse JSON request bodies</span>
app.use(express.json());

<span class="hljs-comment">// Step 4: Mount auth routes</span>
<span class="hljs-comment">// All auth-related routes will start with /api/auth</span>
app.use(<span class="hljs-string">"/api/auth"</span>, <span class="hljs-built_in">require</span>(<span class="hljs-string">"./routes/auth"</span>));

<span class="hljs-comment">// Step 5: Default route to test server</span>
app.get(<span class="hljs-string">"/"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.send(<span class="hljs-string">"Hello World! Your server is working 🚀"</span>);
});

<span class="hljs-comment">// Step 6: Start server on PORT from .env or default 5000</span>
<span class="hljs-keyword">const</span> PORT = process.env.PORT || <span class="hljs-number">5000</span>;
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server running on http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<ul>
<li><p><strong>Load environment variables:</strong> Using <code>dotenv</code> to keep secrets and configuration separate from code.</p>
</li>
<li><p><strong>Connect to MongoDB:</strong> Calls <code>connectDB()</code> from <code>config/db.js</code>.</p>
</li>
<li><p><strong>Middleware:</strong> <code>express.json()</code> allows Express to parse JSON request bodies.</p>
</li>
<li><p><strong>Mount routes:</strong> <code>app.use("/api/auth", ...)</code> registers all authentication routes.</p>
</li>
<li><p><strong>Default route:</strong> A simple GET endpoint to verify server is running.</p>
</li>
<li><p><strong>Start server:</strong> <code>app.listen</code> starts listening on the configured port.</p>
</li>
</ul>
<h3 id="heading-4-how-to-test-your-api">4. How to Test Your API</h3>
<p>In this section, you’ll learn how to test your JWT authentication API using tools like Postman or any HTTP client.</p>
<p>Before testing, make sure your server is running. If it’s not running, open a terminal and run:</p>
<pre><code class="lang-javascript">npm run dev
</code></pre>
<p>or</p>
<pre><code class="lang-javascript">node server.js
</code></pre>
<p>This will start your server on the port defined in <code>.env</code> (default <code>5000</code>).</p>
<p>Make sure your MongoDB is running. If using local MongoDB, start it with:</p>
<pre><code class="lang-javascript">mongod
</code></pre>
<p>or ensure your MongoDB service is active.</p>
<p>Always check the terminal for any errors. If the server or database fails to start, your API requests will not work.</p>
<h4 id="heading-register-a-user">Register a User</h4>
<p>Request:</p>
<pre><code class="lang-javascript">POST http:<span class="hljs-comment">//localhost:5000/api/auth/register</span>
Content-Type: application/json

{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"sumit"</span>,
  <span class="hljs-string">"email"</span>: <span class="hljs-string">"sumit@example.com"</span>,
  <span class="hljs-string">"password"</span>: <span class="hljs-string">"mypassword"</span>
}
</code></pre>
<p>Response:</p>
<pre><code class="lang-javascript">{
  <span class="hljs-string">"message"</span>: <span class="hljs-string">"User registered successfully!"</span>
}
</code></pre>
<p>This sends a POST request to <code>http://localhost:5000/api/auth/register</code> with user details. If successful, you get a confirmation message.</p>
<h4 id="heading-login">Login</h4>
<p>Request:</p>
<pre><code class="lang-javascript">POST http:<span class="hljs-comment">//localhost:5000/api/auth/login</span>
Content-Type: application/json

{
  <span class="hljs-string">"email"</span>: <span class="hljs-string">"sumit@example.com"</span>,
  <span class="hljs-string">"password"</span>: <span class="hljs-string">"mypassword"</span>
}
</code></pre>
<p>Response:</p>
<pre><code class="lang-javascript">{
  <span class="hljs-string">"message"</span>: <span class="hljs-string">"Login successful!"</span>,
  <span class="hljs-string">"token"</span>: <span class="hljs-string">"&lt;JWT_TOKEN&gt;"</span>
}
</code></pre>
<p>This sends a POST request to <code>http://localhost:5000/api/auth/login</code> with email and password. If the credentials are correct, you receive a JWT to access protected routes.</p>
<h4 id="heading-access-protected-route">Access Protected Route</h4>
<p>Request:</p>
<pre><code class="lang-javascript">GET http:<span class="hljs-comment">//localhost:5000/api/auth/profile</span>
Authorization: Bearer &lt;JWT_TOKEN&gt;
</code></pre>
<p>Response:</p>
<pre><code class="lang-javascript">{
  <span class="hljs-string">"message"</span>: <span class="hljs-string">"Welcome to your profile!"</span>,
  <span class="hljs-string">"user"</span>: {
    <span class="hljs-string">"id"</span>: <span class="hljs-string">"..."</span>,
    <span class="hljs-string">"email"</span>: <span class="hljs-string">"sumit@example.com"</span>,
    <span class="hljs-string">"iat"</span>: ...,
    <span class="hljs-string">"exp"</span>: ...
  }
}
</code></pre>
<p>This sends the JWT in the <code>Authorization</code> header using the <code>Bearer</code> scheme.</p>
<ul>
<li><p>Only valid tokens will allow access to this protected route.</p>
</li>
<li><p><code>iat</code> and <code>exp</code> indicate issued-at and expiry time of the token.</p>
</li>
</ul>
<p><strong>Note:</strong> Always include <code>Authorization: Bearer &lt;token&gt;</code> for protected routes.</p>
<h2 id="heading-summary">Summary</h2>
<p>This article gave you a comprehensive overview of JSON Web Tokens (JWTs) and their role in web authentication. It explained the stateless nature of HTTP, the need for tokens, and compares classic session tokens with JWTs.</p>
<p>We covered JWT structure, security mechanisms, and practical implementation using Node.js, Express, and MongoDB. We also discussed security considerations, token management, and how to test a JWT authentication API.</p>
<h3 id="heading-heres-a-summary-of-the-key-points">Here’s a Summary of the Key Points:</h3>
<ol>
<li><p><strong>What is JWT?</strong></p>
<ul>
<li><p>JWT is a JSON-based open standard for securely representing claims between two parties, defined by RFC 7519.</p>
</li>
<li><p>Widely used for authorization in modern web applications and microservice architectures.</p>
</li>
<li><p>Alternative to session tokens for maintaining user state.</p>
</li>
</ul>
</li>
<li><p><strong>Stateless Nature of HTTP</strong></p>
<ul>
<li><p>HTTP does not retain information between requests, requiring each request to carry necessary data.</p>
</li>
<li><p>Tokens (session or JWT) are used to maintain user sessions in dynamic web applications.</p>
</li>
</ul>
</li>
<li><p><strong>Session Tokens</strong></p>
<ul>
<li><p>Classic approach where the server creates and stores a session ID, typically in cookies.</p>
</li>
<li><p>Works well for single-server setups but requires shared storage (for example, Redis) in multi-server environments.</p>
</li>
<li><p>Vulnerable if the shared cache goes down.</p>
</li>
</ul>
</li>
<li><p><strong>JWT: The Modern Solution</strong></p>
<ul>
<li><p>Server sends a signed JSON token to the client, which stores and sends it with each request.</p>
</li>
<li><p>No server-side storage required – all user info is in the token.</p>
</li>
<li><p>Signature ensures validity and integrity.</p>
</li>
</ul>
</li>
<li><p><strong>JWT Structure</strong></p>
<ul>
<li><p>Three parts: Header, Payload, Signature (separated by dots).</p>
</li>
<li><p>Header and payload are Base64 encoded JSON objects. Signature is a hash using a secret key.</p>
</li>
<li><p>Base64 encoding is for convenience, not security.</p>
</li>
</ul>
</li>
<li><p><strong>Decoding JWTs</strong></p>
<ul>
<li><p>Tools like <a target="_blank" href="https://jwt.io/">jwt.io</a> can decode JWTs to show header, payload, and signature.</p>
</li>
<li><p>Sensitive data should not be stored in JWTs, as payload is publicly readable.</p>
</li>
</ul>
</li>
<li><p><strong>JWT Security</strong></p>
<ul>
<li><p>Signature is created using a secret key and cryptographic algorithm.</p>
</li>
<li><p>Server verifies token integrity using the secret key.</p>
</li>
<li><p>JWTs identify users but do not act as passwords.</p>
</li>
</ul>
</li>
<li><p><strong>Security Considerations &amp; Token Management</strong></p>
<ul>
<li><p>If a JWT is compromised, the attacker can impersonate the user until the token expires.</p>
</li>
<li><p>JWTs cannot be directly revoked; blacklisting is used to invalidate compromised tokens.</p>
</li>
<li><p>Session tokens can be invalidated by the server.</p>
</li>
</ul>
</li>
<li><p><strong>JWTs in Different Languages</strong></p>
<ul>
<li><p>JWTs are language-agnostic and can be implemented in Node.js, PHP, and other languages.</p>
</li>
<li><p>Useful for authentication and representing any kind of identity.</p>
</li>
</ul>
</li>
<li><p><strong>Practical Implementation: JWT Authentication with Express + MongoDB</strong></p>
<ul>
<li><p>Step-by-step guide to building a JWT authentication system:</p>
<ul>
<li><p>Project setup and dependencies</p>
</li>
<li><p>Folder structure</p>
</li>
<li><p>Express server initialization</p>
</li>
<li><p>MongoDB connection</p>
</li>
<li><p>User model creation</p>
</li>
<li><p>Password hashing and JWT services</p>
</li>
<li><p>Auth controller and middleware</p>
</li>
<li><p>Auth routes</p>
</li>
<li><p>Main server file</p>
</li>
<li><p>API testing instructions</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Testing the API</strong></p>
<ul>
<li><p>Instructions for registering users, logging in, and accessing protected routes using tools like Postman.</p>
</li>
<li><p>Example requests and responses provided.</p>
</li>
</ul>
</li>
<li><p><strong>Summary &amp; Final Words</strong></p>
<ul>
<li><p>JWTs are secure, stateless, and widely used for authorization.</p>
</li>
<li><p>Security depends on safe token storage and proper management.</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-final-words">Final Words</h2>
<p>You can find all the source code from this tutorial in <a target="_blank" href="https://github.com/logicbaselabs/jwt-auth-demo">this GitHub repository</a>. If it helped you in any way, consider giving it a star to show your support!</p>
<p>Also, if you found the information here valuable, feel free to share it with others who might benefit from it. I’d really appreciate your thoughts – mention me on X <a target="_blank" href="https://x.com/sumit_analyzen">@sumit_analyzen</a> or on Facebook <a target="_blank" href="https://facebook.com/sumit.analyzen">@sumit.analyzen</a>, <a target="_blank" href="https://youtube.com/@logicBaseLabs">watch my coding tutorials</a>, or simply <a target="_blank" href="https://www.linkedin.com/in/sumitanalyzen/">connect with me</a> on LinkedIn.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
