Understanding JWT and Validating Tokens with Expiry Dates
Aamir Khan
Posted on June 25, 2024
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 (.):
- Header
- Payload
- Signature
Example:
xxxxx.yyyyy.zzzzz
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"
}
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
}
Signature
The signature is created using the encoded header, the encoded payload, a secret, and the specified algorithm.
How JWT Works
- Authentication: The server issues a token upon successful authentication.
- Storage: The client stores the token, usually in sessionStorage.
- Subsequent Requests: The client sends the token in the Authorization header of each request.
- 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);
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));
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 });
});
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);
});
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));
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 });
});
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);
});
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));
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 });
});
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.
Posted on June 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.