Search code examples
node.jsjestjsssl-certificatesupertestself-signed-certificate

NodeJS axios request self signed works in browser but not in jest supertest case


I am building a NodeJS app that makes calls to an external API. The external API uses a self-signed certificate. I tried setting the environment variable process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'. This works to ignore the certificate verification when using the app normally. However, a request to the same endpoint does NOT work when calling the NodeJS route with the Jest Supertest agent.

There is a certificate verification error when running the Jest Supertest case. Is there a way to accept self-signed certificates when sending requests using the Supertest agent?

npm test

Error: Error: SSL Error: DEPTH_ZERO_SELF_SIGNED_CERT
          at Object.dispatchError (/home/node/app/node_modules/jsdom/lib/jsdom/living/xhr-utils.js:54:19)
          at EventEmitter.<anonymous> (/home/node/app/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:675:20)
          at EventEmitter.emit (events.js:323:22)
          at Request.<anonymous> (/home/node/app/node_modules/jsdom/lib/jsdom/living/xhr-utils.js:384:47)
          at Request.emit (events.js:311:20)
          at Request.onRequestResponse (/home/node/app/node_modules/request/request.js:948:10)
          at ClientRequest.emit (events.js:311:20)
          at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:603:27)
          at HTTPParser.parserOnHeadersComplete (_http_common.js:119:17)
          at TLSSocket.socketOnData (_http_client.js:476:22) undefined

NodeJS internal route

Works when accessing route via the browser, but not when running Jest Supertest. The internal route is /internal and that works, but when that code subsequently sends a request to the external API that has a self-signed certificate, the self-signed certificate causes a 500 error message.

router.get('/internal', (req, res, next) => {

  // Set request values that are specific to this route
  const requestOptionsData = { method: `GET`, endpoint: `/external` };

  try {
    httpCtrl.makeRequest(requestOptionsData).then(result => {
      if (result.error) {
        return res.status(result.status).json(result.error.message || result.error);
      }
      return res.status(result.status).json(result);
    }).catch((error) => {
      console.error(error);
      return res.status(500).send(error);
    });
  } catch (e) {
    console.error(e);
    return res.status(500).send(e);
  }

});

NodeJS controller

A wrapper function to make axios requests to external API

httpCtrl.makeRequest = async (requestOptionsData) => {

  let result = {};

  // Set request options
  const requestOptions = httpCtrl.setApiRequestOptions(requestOptionsData);

  let response;
  try {
    response = await axios(requestOptions);
  } catch(e) {
    result.error = e.toJSON() || e;
    console.error(result.error);
    result.status = 500;
    return result;
  }

  result.status = response && response.status || 500;
  result.data = response && response.data || {};

  return result;
}

JEST Supertest

Test that causes certificate error

const app = require('../app.js');
const supertest = require('supertest');

describe('API routes', () => {

  it('GET internal NodeJS route', async done => {

      agent
        .get('/internal')
        .set('Accept', 'application/json')
        .send()
        .expect(200)
        .end((err, res) => {
          if (err) {
            return done(err);
          }
          expect(res.status).toBe(200);
          return done();
        });
  });

});

UPDATE:

I tried removing NODE_TLS_REJECT_UNAUTHORIZED and setting rejectUnauthorized to false in the axios agent config but still having the same problem. The connection works when using the app via the browser but does work with supertest.

  const agent = new https.Agent({
    rejectUnauthorized: false
  });

  const options = {
    url: url,
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': `Bearer ${requestOptionsData.jwt}`,
      'Host': process.env.ADMIN_API_BASE_URL
    },
    method: requestOptionsData.method || `GET`,
    httpsAgent: agent
  }

Here is the error with this agent configuration:

Error: Error: self signed certificate
          at Object.dispatchError (/home/node/app/node_modules/jsdom/lib/jsdom/living/xhr-utils.js:54:19)
          at EventEmitter.<anonymous> (/home/node/app/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:675:20)
          at EventEmitter.emit (events.js:323:22)
          at Request.<anonymous> (/home/node/app/node_modules/jsdom/lib/jsdom/living/xhr-utils.js:384:47)
          at Request.emit (events.js:311:20)
          at Request.onRequestError (/home/node/app/node_modules/request/request.js:877:8)
          at ClientRequest.emit (events.js:311:20)
          at TLSSocket.socketErrorListener (_http_client.js:426:9)
          at TLSSocket.emit (events.js:311:20)
          at emitErrorNT (internal/streams/destroy.js:92:8) undefined
    console.error controllers/http.ctrl.js:50

Solution

  • I was able to solve this with the solution in this GitHub issue.

    I solved it by adding testEnvironment: 'node', to jest.config.js file.