JWT Authentication From Scratch

author

By Freecoderteam

Oct 13, 2025

5

image

JWT Authentication From Scratch: A Comprehensive Guide

JSON Web Tokens (JWT) are a popular standard for secure, stateless authentication in web applications. They are widely used because of their simplicity, ease of implementation, and flexibility. In this blog post, we'll explore JWT authentication from the ground up, covering the basics, implementation steps, best practices, and actionable insights.

Table of Contents


Understanding JWT

JWT is a compact and URL-safe means of representing claims to be transferred between two parties. It consists of three parts:

  1. Header: Contains metadata about the token, such as the type of token (JWT) and the signing algorithm (e.g., HMAC SHA256 or RSA).
  2. Payload: Contains claims about the user or the data being transmitted. Claims are key-value pairs that provide information like the user's ID, expiration time, etc.
  3. Signature: Ensures the authenticity of the token. It is generated by combining the header, payload, and a secret key (or public/private key pair).

JWTs are typically sent in the Authorization header with the Bearer scheme, like this:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT Structure

A JWT is a string consisting of three parts, separated by dots (.):

  1. Header: Encoded in Base64 URL format.
  2. Payload: Also encoded in Base64 URL format.
  3. Signature: Computed using the header, payload, and a secret key.

For example:

Header.Payload.Signature

Header Example

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload Example

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Signature Example

The signature is generated using the following formula:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

Why Use JWT?

  • Stateless: JWTs are stateless, meaning the server does not need to store session data. This makes scaling easier.
  • Compact: JWTs are small and can be efficiently transmitted over the network.
  • Flexible: JWTs can carry claims about the user or the data being transmitted.
  • Secure: JWTs are signed and can be verified to ensure authenticity and integrity.

Implementing JWT Authentication

1. Setting Up the Backend

To implement JWT authentication, we'll use Node.js with Express.js for the backend and the jsonwebtoken library to handle JWT generation and verification.

Install Dependencies

First, install the required dependencies:

npm install express jsonwebtoken

Create the Server

Set up a basic Express server:

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const PORT = 3000;
const secretKey = 'your-secret-key'; // Replace with a strong secret key

// Middleware to parse JSON requests
app.use(express.json());

// Simple user database (replace with a real database)
const users = [
  { id: 1, username: 'john', password: 'password123' },
  { id: 2, username: 'jane', password: 'securepassword' }
];

// Endpoint to authenticate users
app.post('/login', (req, res) => {
  const { username, password } = req.body;

  const user = users.find(
    (u) => u.username === username && u.password === password
  );

  if (!user) {
    return res.status(401).json({ message: 'Invalid credentials' });
  }

  const token = jwt.sign({ userId: user.id }, secretKey, { expiresIn: '1h' });

  res.json({ token });
});

// Start the server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

2. Generating and Validating JWT Tokens

Generating a Token

The jsonwebtoken library provides the sign method to generate a JWT:

const token = jwt.sign({ userId: user.id }, secretKey, { expiresIn: '1h' });
  • Payload: { userId: user.id } (contains claims about the user).
  • Secret Key: secretKey (used to sign the token).
  • Options: { expiresIn: '1h' } (token expiration time).

Verifying a Token

To verify a token, use the verify method:

app.use((req, res, next) => {
  const authHeader = req.headers['authorization'];

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  const token = authHeader.split(' ')[1];

  jwt.verify(token, secretKey, (err, decoded) => {
    if (err) {
      return res.status(403).json({ message: 'Invalid token' });
    }

    req.user = decoded;
    next();
  });
});

3. Securing Routes

Now that we have a middleware to verify JWTs, we can secure routes by wrapping them with the authentication middleware.

// Secured route example
app.get('/profile', (req, res) => {
  res.json({ message: `Hello, User ${req.user.userId}!` });
});

Best Practices for JWT

  1. Use Strong Secret Keys: Ensure your secret key is long, random, and kept secure. Avoid hardcoding it in your codebase. Use environment variables instead.

  2. Limit Token Lifespan: Set an expiration time for tokens to reduce the risk of misuse. Short-lived tokens (e.g., 15 minutes) are recommended for sensitive operations.

  3. Use HTTPS: Always use HTTPS to encrypt communication between the client and server. This prevents token interception.

  4. Store Tokens Securely: Store tokens in the HTTPOnly flag to protect against cross-site scripting (XSS) attacks. Avoid storing tokens in local storage.

  5. Implement Blacklisting or Revocation Mechanism: While JWTs are stateless, it's important to have a way to revoke tokens if they are compromised. Use techniques like token revocation lists or short expiration times.

  6. Use Multiple Tokens: Consider using two tokens: an access token (short-lived) and a refresh token (long-lived) to reduce the risk of token expiration.

  7. Validate All Claims: Always validate all claims in the token to ensure they meet your application's requirements.

  8. Use a Secure Algorithm: Use secure algorithms like HS256 (HMAC SHA256) or RS256 (RSA SHA256) for signing tokens.


Common Pitfalls and How to Avoid Them

  1. Hardcoding Secret Keys: Avoid hardcoding secret keys in your code. Use environment variables or a secure key management system.

  2. Not Validating Tokens: Always validate tokens on the server side. Never trust tokens sent from the client without verification.

  3. Using Long-Lived Tokens: Avoid using long-lived tokens. Use short-lived tokens and refresh tokens instead.

  4. Storing Tokens Insecurely: Never store tokens in local storage. Use HTTPOnly cookies or secure local storage mechanisms.

  5. Not Using HTTPS: Always use HTTPS to encrypt communication and prevent token interception.

  6. Not Revoking Compromised Tokens: Implement a token revocation mechanism to handle compromised tokens.


Conclusion

JWT authentication is a powerful and flexible way to secure web applications. By understanding its structure, implementation steps, and best practices, developers can effectively implement JWT authentication in their projects. Remember to prioritize security by using strong secret keys, short-lived tokens, and secure storage mechanisms.

JWTs are not a silver bullet for all authentication problems, but when used correctly, they can significantly enhance the security and scalability of your application.


Resources:

By following the steps and best practices outlined in this guide, you'll be well on your way to implementing secure JWT authentication in your applications. Happy coding! 🚀


Feel free to reach out with any questions or feedback!

Subscribe to Receive Future Updates

Stay informed about our latest updates, services, and special offers. Subscribe now to receive valuable insights and news directly to your inbox.

No spam guaranteed, So please don’t send any spam mail.