javascriptreactjsnode.jshttps

Why the JWT over https make the token expired randomly?


I have created a REACT application that calls some REST APIs to get data from a Node.js server. To enhance security, I am using JWT-based authentication. It is working properly in the development environment. In the production environment, I use an HTTPS-protected Apache server as a proxy server to forward requests to my application.

In this environment, I found that the JWT is not working steadily. Sometimes, the application prompts the "JWT expired" message on the server even though the user has logged in.

Here is my server-side code:

let sendResponse = async (res, action, param, jwt, usrObj, headers = {}) => {
try {
    //res.send(await action(param));
    const sanitized = { ...usrObj };
    delete sanitized.iat;
    delete sanitized.exp;
    res.setHeader('x-access-token', jwt.sign(sanitized));
    let result = await action(param);
    if (Object.keys(headers).length === 0) {
        let returnObj = {
            result
        }
        res.send(returnObj);
    } else {
        for (const [key, value] of Object.entries(headers)) {
            res.setHeader(key, value);
        }
        res.send(result);
    }
} catch (error) {
    console.log(error.message);
    res.status(400).send(error.message);
}

}

where "action" is a function that gets data from my database. The application has an Excel file generation feature, the sendResponse function should support file download.

Here is my JWT class:

import jwt from "jsonwebtoken";
export default class JWT {
    #accessTokenSecret;
    #expirePeriod;
    constructor(secret, expirePeriod) {
        this.#accessTokenSecret = secret;
        this.#expirePeriod = expirePeriod;
    }
    isValid = async token => {
        try {
            return jwt.verify(token, this.#accessTokenSecret);
        } catch (error) {
            throw new Error(`${error.message}`);
        }
    }
    sign = (dataObj) => {
        return jwt.sign(
            dataObj,
            this.#accessTokenSecret,
            {
                expiresIn: this.#expirePeriod,
                algorithm: "HS256"
            });
    }
}

If I open my application to the internet without the Apache server, it back to normal.

So, what's going on?


Solution

  • The problem is in the response header.

    When you are using Apache server as a proxy server, you are passing the JWT token in response headers. The problem with proxy servers is that extra care needs to be done to ensure that the proxy server is forwarding all your response headers from the backend server to frontend.

    Usually, the proxy server sends its own set of response headers and not the headers from backend server. If you are to send the access token in response body, you would notice that your issue is resolved