Part 1: JWT to authenticate downloadable files at Client.
What is JWT ?
As per openid
JSON Web Token (JWT) is a means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS) and/or encrypted using JSON Web Encryption (JWE).
In simple terms, it is just an another way of encoding JSON object and use that encoded object as an access tokens for authentication from the server.
This is the first part of the series of two short post regarding the practical application of JWT.
- JWT for downloading the files at client.
- JWT for server to server authentication
i) Parts of JWT token and explanation of it’s making.
ii) How to authenticate server to server communication via JWT.
What is the problem with downloading?
In most of the frontend applications, we need to download the files from the server but downloading the file is a tricky task. The download URL’s are generally not AJAX calls they are mostly the mimicking of the click events on the anchor tag (see below code).
const downloadFile = (url) => {
//url:BASE_URL/v1/api/downloadX?name=alex;
const link = document.createElement('a');
document.body.appendChild(link);
link.href = url;
link.setAttribute('type', 'hidden');
link.click();
}
If someone opens the above URL (BASE_URL/v1/api/downloadX?name=alex) in the new tab, (s)he will be able to download the file directly without any authentication. We can’t add the XSRF or any other header level verification on the server since it is not an AJAX call and you can’t set the headers to these types of calls.
Solution: JWT to the Rescue.

Let’s modify the above code a bit, before initiating any download actions in the frontend application we should firstly get the token (JWT) from the server.
Client Code
const downloadFile = async (url) => {const { data: { token } } = await axios.get('/v1/api/jwt/get-token');
// axios: Promise based HTTP client for the browser and node.js
// You can make simple fetch for the same.
let finalUrl = url;
if (url.includes('?')) {
finalUrl += `&token=${token}`;
} else {
finalUrl += `?token=${token}`;
}const link = document.createElement('a');
document.body.appendChild(link);
link.href = finalUrl;
link.setAttribute('type', 'hidden');
link.click();
}
Server Code
const jwt = require('jsonwebtoken'); // npm install jsonwebtoken
const router = express.Router();router.get('/v1/api/get-token', (req, res) => {
const secret = process.env.JWT_SECRET; // Secret from environment. // You can store this is DB as well
const token = jwt.sign({}, secret, { expiresIn: 2 }); // 2 seconds expiry for JWT token
return res.json({ token });
});
The first get call in the above client code is AJAX call and it can be secured for any header based verification (XSRF etc). This code will return a JWT token which has an expiry of MAX 2 seconds. We will append this token along with the url of the current download click action and send the token to the server for authentication.
This token will be verified by the middleware on the server as below:
const router = express.Router();
const jwt = require('jsonwebtoken'); // npm install jsonwebtokenconst MESSAGES = {
TOKEN_NOT_FOUND: 'Valid tokens not present ',
TOKEN_EXPIRES: 'Download token expired'
};// Middlware for download Verifier
const JWTDownloadVerifier = async (req, res, next) => {
try {
const token = req.query.token;
if (!token) {
return res.status(400).json({ message: MESSAGES.TOKEN_NOT_FOUND });
}
const secret = process.env.JWT_SECRET;
jwt.verify(token, secret);
} catch (err) {
return res.status(403).json({ message: MESSAGES.TOKEN_EXPIRES });
}
return next();
};router.get('/v1/api/downloadX', JWTDownloadVerifier, (req, res, next) {
const downloadData = getDownloadedData(req.query.name); //ANY DOWNLOAD LOGIC.
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment; filename=downloadX.csv'
});
res.write(downloadData);
})
For every new download call we will generate a temporary token which has an expiry of MAX 2 seconds (ideally it should be < 200 milliseconds) but we have set it to 2 seconds for very slow networks. This token will never be used again and prevent the user from opening the URL in the new tab without the access of the token. Even though if someone copies the token from the network calls (s)he will never be able to use it because of 2 seconds expiry clause which we can reduce anyways
Now our download buttons are secure on the frontend 🙌. Thanks to our <JWT>.
Stay tune for the second blog post for this series.
Please feel free to tweet me @squiroid with any questions, feedback, etc :)
If you like my post and want me to write more, please click on the 👏 to give me some hurray.
EDIT: Here comes the second blogpost JWT to authenticate Servers API’s