Rabi Siddique
687 words
3 minutes
A JSON Web Token
2024-01-14

A JSON Web Token (JWT) is a means of providing authenticated access to users for accessing resources on a server. It has three parts:

  • Header: This section specifies the hashing algorithm being used and declares the type, which is JWT.
{
      "typ": "JWT",
      "alg": "HS256"
}

It is base64 encoded and is the first part of our JWT.

  • Payload: This part contains the data being sent to the client along with important token-related information, such as jti (a unique identifier for the JWT), iat (the issuance time of the JWT), and exp (the expiration time of the token). Like the header, the payload is also base64 encoded and represents the second part of the JWT.

  • Signature: The third and final part of the JWT, the signature, is created by combining the header and payload and then hashing them using a secret key or a public key from the server. This ensures the integrity and authenticity of the token.

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

HMACSHA256(encodedString, 'secret');

How Tokens are Signed#

Tokens can be signed using two different mechanisms:

Symmetric Signatures#

When a JWT is signed using a secret key, it is known as a symmetric signature. This approach is used when there is only one server responsible for both signing and validating the token. The same secret key is used for both generating and validating the token. The token is signed using HMAC (Hash-based Message Authentication Code).

Asymmetric Signatures#

Asymmetric signatures are ideal for distributed environments where multiple applications might need to validate a JWT. Using a secret key in such scenarios would require sharing it across all applications, which could lead to security risks if the key is leaked.

To address this, asymmetric signing employs a private-public key pair. Only one server holds the private key and uses it to generate and sign tokens. These tokens are then shared with the client.

The client can then send this token to any application, which can validate it using the public key. This method uses RSA, an asymmetric encryption and digital signature algorithm, ensuring secure token validation across different applications.

How is a Token Validated?#

Let’s explore how a server validates a JWT. A JWT consists of three parts: a header, a payload, and a signature.

Upon receiving a token, the server extracts the header and payload. It then uses a secret key, or a public key in the case of asymmetric signing, to regenerate the signature based on the header and payload.

If this newly generated signature matches the signature provided within the JWT, the token is considered valid.

To enable token verification, you must provide the secret or the public key from the key pair that was used to sign the JWT:

// verify a token asymmetric
const cert = fs.readFileSync('public.pem'); // get public key
jwt.verify(token, cert, function (err, decoded) {
  console.log(decoded.foo);
});

OR;

// verify a token symmetric using secret
const decoded = jwt.verify(token, 'mySecret');

Code Snippets Related to Token Verification#

const jwt = require('jsonwebtoken');
function verifyToken(req, res, next) {
  const token = req.header('Authorization');
  if (!token) return res.status(401).json({ error: 'Access denied' });
  try {
    const decoded = jwt.verify(token, 'your-secret-key');
    req.userId = decoded.userId;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
}

module.exports = verifyToken;
const express = require('express');
const router = express.Router();
const User = require('../models/User');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');

// User registration
router.post('/register', async (req, res) => {
  try {
    const { username, password } = req.body;
    const hashedPassword = await bcrypt.hash(password, 10);
    const user = new User({ username, password: hashedPassword });
    await user.save();
    res.status(201).json({ message: 'User registered successfully' });
  } catch (error) {
    res.status(500).json({ error: 'Registration failed' });
  }
});

// User login
router.post('/login', async (req, res) => {
  try {
    const { username, password } = req.body;
    const user = await User.findOne({ username });
    if (!user) {
      return res.status(401).json({ error: 'Authentication failed' });
    }
    const passwordMatch = await bcrypt.compare(password, user.password);
    if (!passwordMatch) {
      return res.status(401).json({ error: 'Authentication failed' });
    }
    const token = jwt.sign({ userId: user._id }, 'your-secret-key', {
      expiresIn: '1h',
    });
    res.status(200).json({ token });
  } catch (error) {
    res.status(500).json({ error: 'Login failed' });
  }
});

Further Reading#

A JSON Web Token
https://rabisiddique.com/posts/json-web-token/
Author
Rabi Siddique
Published at
2024-01-14