Difficulty setting Cookie when using an ngrok tunnel (Express server on Node.js, React app frontend)

Viewed 366

As outlined in the title, I am having difficulty setting a http cookie to be used for auth purposes when tunnelling using ngrok.

The following code works fine (obviously with the relevant endpoints specified) when i am running a query from from localhost to a localhost endpoint in my dev environment but breaks down as soon as i start to query the ngrok tunnel endpoint.

Frontend api query (simplified as part of larger application)

function fetchRequest (path, options) {
  const endpoint = 'http://xxx.ngrok.io'; // the ngrok tunnel endpoint
  return fetch(endpoint + path, options)
    .then(res => {
      return res.json();
    })
    .catch((err) => {
      console.log('Error:', err);
    });
}

function postRequest (url, body, credentials='include') {
  return fetchRequest(`${url}`, {
    method: 'POST',
    withCredentials: true,
    credentials: credentials,
    headers: {'Content-Type': 'application/json', Accept: 'application.json'},
    body: JSON.stringify(body)
  });
}

// data to be passed to backend for authentication
let data = {pin: pin, username : username};

postRequest('/',data)

Express server on Node.js with ngrok tunnel (app.js)

const express = require('express')
const session = require('express-session')
const cors = require('cors')
const router = require('./router');
const tunnel = require('./ngrok')

const app = express()
const port = process.env.PORT || 4001;

app.use(cors({
  origin: 'http://localhost:3000'
  credentials: true,
}))
app.use(express.json());

const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: {
    httpOnly: true,
    expires: expiryDate
    // sameSite: 'none'
    // secure: true 
  }
}))
app.use(router)

let useNGROK = true;

if (useNGROK) {
  app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
  })
  tunnel.createHTTPtunnel().then((url) => {
    console.log(`New tunnel created with endpoint: ${url}`)
  });
} else {
  app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
  })
}

Ngrok configuration (ngrok.js)

const ngrok = require('ngrok');
const find = require('find-process');
const port = process.env.PORT || '3000';

const tunnel = {
  createHTTPtunnel: async function () {

    const list = await find('name', 'ngrok');

    if (list.length > 0) {
      let api = ngrok.getApi();
      if (api == null) {
        this.kill_existing_tunnel();
      } else {
        let open_tunnels = await ngrok.getApi().listTunnels();
        return open_tunnels.tunnels[0].public_url;
      }
    }

    let ngrok_config = {
        proto: 'http',
        bind_tls: false,
        name: process.env.NGROK_NAME,
        hostname: process.env.NGROK_CUSTOM_DOMAIN,
        // host_header: 'rewrite',
        authtoken: '',
        region: 'eu',
    };

    return ngrok.connect({ ...ngrok_config, addr: port });
  },

  kill_existing_tunnel: async () => {
    const list = await find('name', 'ngrok');
    list.forEach((p) => {
      try {
        process.kill(p.pid);
        console.log(`Killed process: ${p.name} before creating ngrok tunnel`);
      } catch (e) {
        console.log(e);
      }
    });
  }
  
}

module.exports = tunnel;

** router & controller (router.js & controller.js respectively) **

*router.js*
const router = require('express').Router();
const example = require('./controller')

router.post('/', example.authenticate);

module.exports = router;

*controller.js*
async function authenticate (req, res) {
  try {
    res.send(JSON.stringify('trying to send cookie'))
  } catch (e) {
    console.log('Error', e)
    res.sendStatus(500)
  }
}

module.exports = {
  authenticate
};


The following information is provided when inspecting the Set-Cookie response header in the network requests:

This Set-Cookie header didn’t specify a “SameSite” attribute and was defaulted to “SameSite=Lax” and was blocked because it came from a cross-site response which was not the response to a top-level navigation. The Set-Cookie had to have been set with “SameSite=None” to enable cross site usage.

Attempted fix 1// If I add the following options to the cookie {sameSite: ‘none’, secure:true}, amend the ngrok config to set {bind_tls: true} and run https on my front end (using a custom SSL certificate as per the create react app documentation), and query the https tunnel, then no cookie is received in the response from the server at all (request is sent and response 200 is received but with no cookie).

Attempted fix 2// I also tried to change the host_header option to rewrite in the ngrok config (to mirror a response from localhost rather than from ngrok) and this did not work.

Any help would be much appreciated as I have little experience and I am stuck!

0 Answers
Related