DEV Community

Aamir Khan
Aamir Khan

Posted on

Understanding JWT and Validating Tokens with Expiry Dates

JSON Web Tokens (JWT) are widely used for secure data transmission and authentication in modern web applications. This guide will provide an overview of JWT and demonstrate how to validate tokens with expiry dates, including examples with Microsoft Azure AD and Azure AD B2C tokens.

What is JWT?

JWT stands for JSON Web Token. It is a compact, URL-safe means of representing claims to be transferred between two parties. The token is digitally signed, so the information is verifiable and trusted.

Structure of JWT

A JWT consists of three parts separated by dots (.):

  1. Header
  2. Payload
  3. Signature

Example:

xxxxx.yyyyy.zzzzz
Enter fullscreen mode Exit fullscreen mode
Header

The header typically consists of two parts: the type of token (JWT) and the signing algorithm (e.g., HMAC SHA256 or RSA).

Example:

{
  "alg": "HS256",
  "typ": "JWT"
}
Enter fullscreen mode Exit fullscreen mode
Payload

The payload contains the claims, which are statements about an entity (typically the user) and additional data. Common claims include sub (subject), name, and exp (expiration time).

Example:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "exp": 1516239022
}
Enter fullscreen mode Exit fullscreen mode
Signature

The signature is created using the encoded header, the encoded payload, a secret, and the specified algorithm.

How JWT Works

  1. Authentication: The server issues a token upon successful authentication.
  2. Storage: The client stores the token, usually in sessionStorage.
  3. Subsequent Requests: The client sends the token in the Authorization header of each request.
  4. Verification: The server verifies the token's signature and decodes the payload to authorize the request.

Example with JavaScript

Let's see an example of creating, sending, and verifying a JWT in a Node.js application, including validating the token based on its expiry date.

Server Side (Node.js)

const jwt = require('jsonwebtoken');
const secretKey = 'your-256-bit-secret';

// Create a token
const token = jwt.sign(
  { 
    id: 1, 
    username: 'john.doe', 
    email: 'john.doe@example.com' 
  }, 
  secretKey, 
  { 
    algorithm: 'HS256', 
    expiresIn: '1h' // Token expires in 1 hour
  }
);

console.log(token);
Enter fullscreen mode Exit fullscreen mode

Client Side (JavaScript)

// Store the token in sessionStorage
sessionStorage.setItem('token', 'your-generated-jwt-token');

// Send the token with a request
const token = sessionStorage.getItem('token');
fetch('/api/protected-endpoint', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Enter fullscreen mode Exit fullscreen mode

Server Side (Node.js) - Verifying Token with Expiry Date

const jwt = require('jsonwebtoken');
const secretKey = 'your-256-bit-secret';

// Middleware to verify the token
const verifyToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  if (authHeader) {
    const token = authHeader.split(' ')[1];
    jwt.verify(token, secretKey, (err, user) => {
      if (err) {
        if (err.name === 'TokenExpiredError') {
          return res.status(401).send('Token has expired');
        }
        return res.status(403).send('Invalid token');
      }
      req.user = user;
      next();
    });
  } else {
    res.status(401).send('Authorization header not found');
  }
};

// Protected route
app.get('/api/protected-endpoint', verifyToken, (req, res) => {
  res.json({ message: 'This is a protected endpoint', user: req.user });
});
Enter fullscreen mode Exit fullscreen mode

Example with Microsoft (Azure AD) Token

Microsoft's Azure Active Directory (Azure AD) issues JWT tokens for authentication and authorization. Let's see how to acquire and verify an Azure AD token, including validating the token based on its expiry date.

Step 1: Acquiring an Azure AD Token

To acquire a token, you must authenticate against Azure AD. You can use Microsoft's msal library for this.

Client Side (JavaScript)

import * as msal from '@azure/msal-browser';

const msalConfig = {
  auth: {
    clientId: 'your-client-id',
    authority: 'https://login.microsoftonline.com/your-tenant-id',
    redirectUri: 'http://localhost:3000'
  }
};

const msalInstance = new msal.PublicClientApplication(msalConfig);

const loginRequest = {
  scopes: ["User.Read"]
};

msalInstance.loginPopup(loginRequest)
  .then(response => {
    console.log('Access Token:', response.accessToken);
    sessionStorage.setItem('msalToken', response.accessToken);
  })
  .catch(error => {
    console.error('Login failed:', error);
  });
Enter fullscreen mode Exit fullscreen mode

Step 2: Sending the Token with Requests

Client Side (JavaScript)

const token = sessionStorage.getItem('msalToken');
fetch('/api/protected-endpoint', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Enter fullscreen mode Exit fullscreen mode

Step 3: Verifying the Token on the Server

Server Side (Node.js) - Verifying Token with Expiry Date

const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

// Middleware to verify the token
const verifyAzureADToken = async (req, res, next) => {
  const authHeader = req.headers['authorization'];
  if (!authHeader) {
    return res.status(401).send('Authorization header not found');
  }

  const token = authHeader.split(' ')[1];
  const decodedToken = jwt.decode(token, { complete: true });

  if (!decodedToken) {
    return res.status(401).send('Invalid token');
  }

  const client = jwksClient({
    jwksUri: 'https://login.microsoftonline.com/your-tenant-id/discovery/v2.0/keys'
  });

  const kid = decodedToken.header.kid;
  client.getSigningKey(kid, (err, key) => {
    if (err) {
      return res.status(401).send('Unable to get signing key');
    }

    const signingKey = key.getPublicKey();
    jwt.verify(token, signingKey, (err, user) => {
      if (err) {
        if (err.name === 'TokenExpiredError') {
          return res.status(401).send('Token has expired');
        }
        return res.status(403).send('Invalid token');
      }
      req.user = user;
      next();
    });
  });
};

// Protected route
app.get('/api/protected-endpoint', verifyAzureADToken, (req, res) => {
  res.json({ message: 'This is a protected endpoint', user: req.user });
});
Enter fullscreen mode Exit fullscreen mode

Example with Azure AD B2C

Azure AD B2C (Business to Consumer) is a cloud identity management solution for your consumer-facing web and mobile applications. It enables you to customize and control how customers sign up, sign in, and manage their profiles.

Step 1: Acquiring an Azure AD B2C Token

Client Side (JavaScript)

import * as msal from '@azure/msal-browser';

const msalConfig = {
  auth: {
    clientId: 'your-client-id',
    authority: 'https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/B2C_1_signupsignin1',
    redirectUri: 'http://localhost:3000'
  }
};

const msalInstance = new msal.PublicClientApplication(msalConfig);

const loginRequest = {
  scopes: ["https://your-tenant-name.onmicrosoft.com/api/read"]
};

msalInstance.loginPopup(loginRequest)
  .then(response => {
    console.log('Access Token:', response.accessToken);
    sessionStorage.setItem('b2cToken', response.accessToken);
  })
  .catch(error => {
    console.error('Login failed:', error);
  });
Enter fullscreen mode Exit fullscreen mode

Step 2: Sending the Token with Requests

Client Side (JavaScript)

const token = sessionStorage.getItem('b2cToken');
fetch('/api/protected-endpoint', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${token}`
  }
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
Enter fullscreen mode Exit fullscreen mode

Step 3: Verifying the Token on the Server

Server Side (Node.js) - Verifying Token with Expiry Date

const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

// Middleware to verify the token
const verifyB2CToken = async (req, res, next) => {
  const authHeader = req.headers['authorization'];
  if (!authHeader) {
    return res.status(401).send('Authorization header not found');
  }

  const token = authHeader.split(' ')[1];
  const decodedToken = jwt.decode(token, { complete: true });

  if

 (!decodedToken) {
    return res.status(401).send('Invalid token');
  }

  const client = jwksClient({
    jwksUri: 'https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/discovery/v2.0/keys'
  });

  const kid = decodedToken.header.kid;
  client.getSigningKey(kid, (err, key) => {
    if (err) {
      return res.status(401).send('Unable to get signing key');
    }

    const signingKey = key.getPublicKey();
    jwt.verify(token, signingKey, (err, user) => {
      if (err) {
        if (err.name === 'TokenExpiredError') {
          return res.status(401).send('Token has expired');
        }
        return res.status(403).send('Invalid token');
      }
      req.user = user;
      next();
    });
  });
};

// Protected route
app.get('/api/protected-endpoint', verifyB2CToken, (req, res) => {
  res.json({ message: 'This is a protected endpoint', user: req.user });
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

JWT provides a powerful and secure way to handle authentication and authorization in modern web applications. By understanding its structure and usage, you can efficiently implement JWT in your applications, including those integrating with Microsoft Azure AD and Azure AD B2C. This guide has shown you how to create, send, and verify JWTs in a Node.js environment, how to validate tokens based on their expiry date, and how to work with Azure AD and Azure AD B2C tokens using sessionStorage for client-side token storage.

Top comments (0)