auth0 / express-jwt

connect/express middleware that validates a JsonWebToken (JWT) and set the req.user with the attributes

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

node.js x react x Auth0 Error: connect ECONNREFUSED 127.0.0.1:80 when using docker-compose but not when using docker run

NoeJuzaAltis opened this issue · comments

Description

Provide a clear and concise description of the issue, including what you expected to happen.

I'm using a React front end (served by my node.js back end) to make secured requests to my express-jwt secured backend (see code samples below).
Everything worked fine in the development and tests phases (using node alone and simple docker runs).
But when I started using docker-compose to deploy my whole stack (I have redis,mongo and influx containers that need to interact with maximum security, easy and fast configuration customization hence the use of docker-compose) everything went wrong and I couldn't make any secured requests to my back end as it was trowing 500s!
It says it wants (but fails) to connect to "127.0.0.1:80", my web app (and its container) is running on port 8080 and I never call anything remotely similar to the localhost on port 80 anywhere (not in front end nor back end).

Expected behavior: Secure api request with valid token returns the datas to the authorized user as it does with node standalone and docker run.
Current behavior: With docker-compose access to front end and unprotected routes is possible and works great but access to express-jwt protected routes throws error (see log sample below).

Since the issue is appearing only for express-jwt routes I'm pretty sure the issue comes from this lib and/or one of its components.

Reproduction

Detail the steps taken to reproduce this error, what was expected, and whether this issue can be reproduced consistently or if it is intermittent.

The issue happens consistently every time I run my web container with docker-compose. but not when I run it with a simple docker run
Steps to reproduce:

  1. deploy an express-jwt protected api with docker-compose
  2. try to access a protected route

Where applicable, please include:

  • Code sample to reproduce the issue

Back end code sample

import path from 'path';
import express from 'express';
import expressStaticGzip from "express-static-gzip";
import cors from 'cors';
import dotenv from 'dotenv';
import jwt from 'express-jwt';
import jwks from 'jwks-rsa';

const app = express(); // defining an instance of express lib in the object "app"
dotenv.config() // getting environement variables from ".env" or the real envs of the system
const __dirname = path.resolve();

function shouldCompress (req, res) {
  if (req.headers['x-no-compression']) {
    // don't compress responses with this request header
    return false
  }

  // fallback to standard filter function
  return compression.filter(req, res)
}

var jwtCheck = jwt({ // Creating authentication validator as described by Auth0's Doc.
    secret: jwks.expressJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 1000,
        jwksUri: process.env.JWKSURI
  }),
  audience: process.env.AUDIENCE,
  issuer: process.env.ISSUER,
  algorithms: [process.env.ALGORITHMS]
});
app.use(cors());
app.use(compression({ filter: shouldCompress }));

//-------------------------------------------------------------------------------------------------------------------------------------------------
// secured API endpoint example
app.route("/api/secure/v1/test")
.get(jwtCheck, (err,req) =>{
    res.status(200).send({"status":"working lol"})
})
app.use(function (err, req, res, next) {    // adding error handling to the express server to prevent it from "crashing" when someone tries to query secured API without a token.
    console.log("passing through error handling middleware")
    if (err) {
        console.log("there was an error. Error: ", err)
        if(err.status == 401)    // if error is token not found
        {
            console.log("secured access attempted with no or an invalid auth token provided. Provided token:", req.headers.authorization )
            res.status(401).redirect("/errors?code=401") // send error to front with status 401 Unauthorized
        }else{
            res.status(err.status).send("error %d", err.status)
        }
    }else{
        next();
    }
});
app.use("/", expressStaticGzip(path.join(__dirname,"client","build"), {enableBrotli: true, index: false}));
app.get("*", (req, res) => {
    //console.log("passed through front end routing")
    res.sendFile(path.join(__dirname,'client' , 'build', 'index.html'));
});
// make the webserver listen on port 8080
console.log("static content served from: " + path.join(__dirname, 'client', 'build', 'static') )
app.listen(8080, () => console.log('API and front are running on http://localhost:8080/'));

Dockerfile for web service

FROM node:16

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

COPY client ./client

RUN npm run build
# If you are building your code for production
# RUN npm ci --only=production

# Bundle app source
COPY . .

EXPOSE 8080
CMD [ "npm", "run", "start" ]

docker-compose web service description:

web:
    restart: unless-stopped
    image: registry.gitlab.com/someprivateImage
    expose:
      - 8080
    ports:
      - "8080:8080"
    links:
      - "cache:cache"
    depends_on:
      - api
      - cache
    environment:
      - PORT=8080
      - DOMAIN="example.com"
      - CLIENTID="example"
      - JWKSURI="example.com/jwks.json"
      - AUDIENCE="example.com/api"
      - ISSUER="example.com"
      - ALGORITHMS="RS256"
      - REDISHOST=cache
      - REDISPORT=6379
    networks:
      - tpi-noejuza-net
  • Log files (redact/remove sensitive information)

logs when trying to access secured endpoint:

passing through error handling middleware
there was an error. Error:  Error: connect ECONNREFUSED 127.0.0.1:80
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1157:16) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 80
}

Environment

Please provide the following:

  • Version of this library used: "express-jwt": "^6.1.1"
  • Version of the platform or framework used, if applicable: node v16.15.0
  • Other relevant versions (language, server software, OS, browser): Manjaro Linux x86_64 kernel : 5.15.41-1-MANJARO
  • Other modules/plugins/libraries that might be involved:(see import section of code sample)+ Docker Compose version 2.5.1 Docker version 20.10.16

Thank you all in advance for your precious help!

I am not sure I understand this. maybe the jwks uri?

in the example you shared it say JWKSURI="example.com/jwks.json"

I am not sure I understand this. maybe the jwks uri?

in the example you shared it say JWKSURI="example.com/jwks.json"

Hello, sorry for the very late answer I was under the water for a long time.
Thank you for your answer but i unfortunately think it is not a problem here.
In my shared version of the code i transformed instance-specific informations to some random placeholder so those might be incorrect, in my docker-compose I set it as follows:
JWKSURI="https://my-auth0-instance.eu.auth0.com/.well-known/jwks.json"
As defined by the official Auth0 documentation (step 2)

Ok, I am not sure how to help. In the first message you stated that the error is:

there was an error. Error:  Error: connect ECONNREFUSED 127.0.0.1:80
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1157:16) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 80
}

This library in particular doesn't make ANY http request nor establish any tcp connection. In addition to that you mention that it works with certain configuration of docker, but this library doesn't have any specific code for docker itself.

The JWKS library does make a request, it will help if you can check what value are you exactly getting in that environment variable. Are you sure you are specifying the https:// protocol for instance?

Maybe introducing a console.dir(process.env); at the start of the program could help you to figure out the difference between both configurations (docker run vs docker compose)

Could you solve the problem? I'm facing that problem too

I also faced the same issue. The problem was with environment variables in .env file.

Changed the following,
JWT_JWKS_URI="http://myjwksuri.com
to
JWT_JWKS_URI=HTTP://myjwksuri.com

On the this line if the URL has two double quotes like this ""http://myjwksuri.com"". It redirects the requests to localhost:80. Not sure why.

Due to the extra double quotes the request module is making the request on localhost:80 instead of the specified JWKS url.