CORS and the Infura API Secret

Introduction

This note proposes one way to allow the Infura API Secret to be used in browser based JavaScript without causing a CORS error.

Another approach is documented here.

Background

When you use your Infura API key to make requests it is used to identify you to Infura and keep track of your usage, etc.

curl https://mainnet.infura.io/v3/**YOUR-API-KEY** \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params": [],"id":1}'

For extra security you can enable “API Key secret required” for the API key and send a basic authentication header with each request. This can be generated by curl with the —user option. More information can be found here.

This is fine for backend code but for browser based JavaScript this leaves the API key secret exposed in the code to anyone willing to do a bit of digging.

In fact Infura will fail to return the magic CORS headers if an authentication header is sent. The request from the browser then fails with a CORS error.

If your frontend communicates with a nodejs (maybe express) backend then the solution is to do your Infura requests from the backend where the API secret can be kept secure. But what if your architecture is front end only?

A Solution

One possible solution is to implement a minimal backend that handles the CORS headers so that the browser is kept happy. But this would still leave the API secret exposed in the front-end code.

So I thought, what about a proxy server? The front-end app talks to the proxy server and the proxy server adds an authentication header to each request. This way the API secret is kept secure in backend code.

This code uses the http-proxy node package:

var http = require('http'),
    httpProxy = require('http-proxy');

infura_api_key = process.env['INFURA_API_KEY'];
infura_secret = process.env['INFURA_SECRET'];
//
// Create your proxy server.
//
proxy = httpProxy.createProxyServer({});

// proxy.on('proxyRes', function(proxyRes, req, res, options) {
//   console.log('RAW Response from the target', JSON.stringify(proxyRes.headers, true, 2));
// });

var server = http.createServer(function(req, res) {
  // You can define here your custom logic to handle the request
  // and then proxy the request.
  proxy.web(req, res, {
  target:'https://mainnet.infura.io',
  changeOrigin: true,
  auth: infura_api_key + ':' + infura_secret,
  });
})

console.log("listening on port 8000");
server.listen(8000);

See: https://www.npmjs.com/package/http-proxy

The code first get’s the Infura API key and secret from the environment (you could also use the dotenv package for this).

Then it creates a proxy server object and, later, creates the http server with the options it needs to know the real target of the requests, etc. The important part is adding the “auth:” header to requests between the proxy and Infura. This way the browser code can be written to not use an “auth:” header because it is added in by the proxy on the way through.

Note above, we create the proxy server in two steps to allow the flexibility to add event handlers to log or change the requests or the responses. See the proxy.on example above.

If, for example, the above code is started on my local machine with:

~$ node index.js
listening on port 8000

Then I would write my browser based JavaScript to send requests to:

http://localhost:8000/v3/**YOUR-API-KEY**

You also have the option to access the proxy server on https. However, you will need to generate appropriate SSL certificates and add them as an option when the proxy server is created. See the http-proxy documentation for details.

Conclusion

This might be the solution for pure browser based JavaScript apps using Infura where there is a need to fully secure the access with the API Secret.

8 Likes