How I enabled CORS for any API on my Single Page App

Reading Time: 7 minutes

In this blog post I’ll show you how I used free services available to anyone to build a little proxy server for my app to overcome certain CORS limitations for my Single Page App.

I built Chisel to help with some repetitive API responses composition and manipulation that I was doing at work.

It’s a single page app that allows you to perform requests against any API endpoint and compose results to extract only what you need. It also allows for CSV exports. Pretty straightforward.

Being it still in its earliest days I decided that I wanted to build it with the simplest architecture in order for me to be able to iterate quickly. I went for the JAMstack, built it in React and deployed on Netlify.

Since it doesn’t have a back-end server it talks to, anything you do stays on your machine. Unfortunately, not all APIs allow for cross-origin requests so, in certain cases, you won’t be able to perform any request from your browser unless you enable the proxy functionality.

Proxy feature on chisel.cloud

What happens if you don’t is that your browser will attempt a CORS preflight request which will fail if the API doesn’t respond with the expected headers.

CORS preflight request failure

What is CORS and when is it a problem for your Single Page App?

From the MDN documentation:

Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own.

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Now, there are certain requests, called Simple Requests, that don’t trigger CORS checks. Unfortunately, these type of requests are quite limited and don’t allow to pass certain headers like the Authorization one (e.g. a basic-auth request). You can read more about these type of requests here.

For this reason, we’re going to allow a good set of HTTP methods and headers to pass through our proxy and return back the response as unchanged as possible.

The bulk of the work will be configuring the right set of Access-Control-Allow-* headers to be returned back to the browser when CORS preflighted checks are performed. I recommend you have a look at the MDN Documentation to learn more about CORS as it is quite comprehensive.

The proxy

In order to allow any request to pass the CORS preflight checks I built a simple proxy server that returns the expected headers to the browser and passes through the requests to the destination server.

You can find the source code for it on Github, but let’s go through the steps to build your own for free.

Setting up NGINX

The proxy itself is a simple instance of NGINX configured with a server to allow for proxied request to a dynamic destination.

In order to be able to run NGINX on Heroku we have to make some changes to run it as non-privileged user.

We’re basically making sure that NGINX will try to write to unprivileged writeable locations: this is because Heroku enforces that our container runs as non-root. You can read more about it here.

Accounting for any URL

The second aspect of this configuration is actually defining our dynamic proxy: we will translate requests to any URL so that they will expose the right CORS information.

The main complexity of the Chisel case resides in the fact that we want to allow any URL to be proxied. This is because we won’t know in advance what URL the user will type in, of course.

The way NGINX allows for setting up the proxy functionality is through the proxy_pass directive:

Sets the protocol and address of a proxied server and an optional URI to which a location should be mapped. As a protocol, “http” or “https” can be specified.

The NGINX documentation

In order to be able to specify the URL to pass to dynamically I decided to go with a custom header: X-Chisel-Proxied-Url. This way Chisel will use that header to tell the proxy which destination to proxy through to.

proxy_pass $http_x_chisel_proxied_url;

The $ symbol in NGINX is used to reference variables and the HTTP headers get automatically converted to $http_ prefixed variables using the above syntax.

There’s quite a bit of things to go through in this NGINX server configuration. Let’s start with the location / block first.

The first bit in there is the if statement: it handles the CORS preflighted requests case and it basically allows for a bunch of HTTP methods and headers by default. It restricts everything to the https://chisel.cloud Origin, just because I don’t want my proxy to be used by other applications.

  • proxy_redirect off: I disabled redirects for now. I’m still not sure how I’m going to handle them so I decided to turn them off until I can find a use case for them.
  • proxy_set_header Host $proxy_host: this is simply forwarding the destination host as the Host header. This is a requirement for valid HTTP requests through browsers. This value will be exactly the same as the one being set for proxy_pass.
  • proxy_set_header X-Real-IP $remote_addr: here we’re simply taking care of forwarding the client IP through to the destination.
  • proxy_pass $http_x_chisel_proxied_url: this is the real important bit of the whole configuration. We’re taking the header coming in from the Chisel client application and setting it as the URL to pass through to. This is effectively making the dynamic proxy possible.
  • proxy_hide_header 'access-control-allow-origin': this, together with the following add_header 'access-control-allow-origin' 'https://chisel.cloud' is basically making sure to override whatever Access-Control-Allow-Origin header is coming back from the destination server with one that only allows requests from our Chisel application.

Finally, the top two directives.

  • resolver: this is needed so that NGINX knows how to resolve the names of the upstream servers to proxy through to. In my case I picked a public free DNS. You can pick yours from here.
  • listen $__PORT__$ default_server: this one, instead, is the directive that makes everything possible using Docker on Heroku. We will have a look at it later in this blog post, so keep reading!

Building the container image

As mentioned above, I’m going to use NGINX’s base image.

Dockerfile

The Dockerfile is pretty simple. We’re replacing the default nginx.conf with our own to make sure that NGINX can run unprivileged. We’re also copying our proxy server configuration.

As you can see I have named the file as proxy.conf.tpl. I’ve done this to be explicit about the fact that the file is not ready to be used as is. We will have to dynamically edit the port it is going to listen on at runtime before starting NGINX.

As clarified in the documentation, Heroku expects the containers to be able to listen on the value specified within the $PORT environment variable. The solution we’re using here, then, is making sure to replace the $__PORT__$ placeholder I have included in the configuration with the actual content of the $PORT environment variable.

Setting up Heroku

We’re almost there. Now we need to configure our application so that we can deploy our container straight from our repository.

Create a new lovely app on Heroku so that we can prepare it to work with containers.

Next, let’s configure the app to work with container images. I haven’t found a way to do it through the dashboard so let’s go ahead with the command line.

Now add a simple heroku.yml file to your repository so that Heroku knows what to do to build the image.

build:
  docker:
    web: Dockerfile

Simple as that.

Now, in the Deploy tab of your application dashboard, make sure you connect your repository to the app: this way you’ll be able to deploy automatically.

Heroku Deploy section – Github connection

Your proxy is finally ready to go. Once you kick off the deploy you’ll be able to see it start up in the application logs as follows.

Startup application logs

As you can see, the process is being started using the command we have specified through the CMD directive and the PORT value is being injected by Heroku.

With the proxy up you’ll now be able to forward your requests through the proxy. As mentioned above, you will need to use the custom X-Chisel-Proxied-Url header (or whatever header you decide to configure for your proxy) to specify the original URL the user intended to hit.

As you can see from the animated gif below, the proxy feature allows to overcome the CORS limitation when hitting the Nager.Date API from Chisel.

The Chisel proxy in action

Conclusion

We have just built a proxy server reusing open-source technology. This allows us to keep our Singe Page App separate from the server logic that’s needed to overcome the CORS limitations.

In general, CORS is one of the security measures your browser employs to mitigate certain opportunity for hijacking your website to perform unintended activity. Even if we have just examined an opportunity for bypassing this limitation, always think twice about whether it is appropriate or not for your use case.

I hope you enjoyed this quick walk-through to build your own free proxy server. Don’t forget to follow me on Twitter for more content like this.

How I used Chisel to pull Gitlab pipelines stats

Reading Time: 4 minutes

I built chisel.cloud in my spare time to automate something I did to derive insights about my Gitlab pipeline times.

In this blog post I’m going to show you how I did it in the hope that it might be useful to you too.

chisel app main screen

As you can see from the picture above, Chisel is still pretty early stage. I decided to publish it anyway because I’m curious to know whether something like this can be useful to you too or not.

Understanding deployment time

The goal of this exercise was for me to better understand the deployment time (from build to being live in production) of my project and have a data-driven approach as to what to do next.

Since the project in question uses Gitlab CI/CD, I thought of taking advantage of its API to pull down this kind of information.

Gitlab Pipelines API

The Gitlab pipelines API is pretty straightforward but a few differences between the /pipelines and the /pipelines/:id APIs means that you have to do a little composition work to pull down interesting data.

Here’s how I did it.

1. Pull down your successful pipelines

First thing I did was fetching the successful pipelines for my project.

chisel app first screen

As you can see, this API returns minimal information about each pipeline. What I needed to do next in order to understand pipeline times was to fetch further details for each pipeline.

Chisel – Transform

Chisel provides a handy transformation tool that uses JMESPath to help you manipulate the JSON returned by the API you are working with. I used it to extract the pipeline IDs from the returned response.

chisel app second screen

Chisel shows you an live preview of your transformation. Something as simple as [*].id is enough for now. The result is an array of pipeline IDs.

Right after obtaining all the IDs I need I can apply another transformation to turn those IDs into pipeline objects with all the relevant information I need for my stats.

Chisel has another kind of transformation type called Fetch that helps you transform the selected values into the result of something fetched from a URL.

chisel app third screen

In particular, you can use the ${1} placeholder to pass in the mapped value. In my case, each ID is being mapped to the /pipelines/${1} API.

The result is pretty straightforward.

chisel app fourth screen

2. Filter out what you don’t need

As you can see, some of the returned pipelines have a before_shaof value 0000000000000000000000000000000000000000. Those are pipelines triggered outside of merges into master so I’m not interested in them.

Filtering those out is as simple as [?before_sha != '0000000000000000000000000000000000000000]

chisel app fifth screen

The transformation history

As you can see, on the right of the screen there’s a little widget that shows you the transformations you have applied. You can use it to go back and forth in the transformation history and rollback/reapply the modifications to your data.

chisel app transformation history

3. The last transformation

The last transformation I need to be able to start pulling out useful information has to turn my output into a set of records.

chisel app sixth screen

I’m selecting only a few fields and turning the result into an array of array. This is the right format to be able to export it as a CSV.

chiel app csv download screen

Google Sheets

Finally, I can upload my CSV export to Google Sheets and plot the information I need.

google sheets import

Conclusion

Chisel is still at its earliest stage of development and it is pretty much tailored on my specific use case but if you see this tool can be useful to you too, please head to the Github repo and suggest the improvements you’d like to see.

If you liked this post and want to know more about Chisel, follow me on Twitter!


Featured image by Dominik Scythe on Unsplash

Workaround Windows Tray Area Item Preference

Reading Time: 4 minutes

Introduction

I’m not an experienced Windows developer.
I had to make it clear 🙂

Today I had the chance to implement kind of a nasty hack on Windows.
I had to make my application tray icon always visibile, at least by default. I swear I honor user preference then. I know this is one of those don’tswhen working with Windows API since it is clearly stated in the docs developers have no control over the notification area. But sometimes you feel it deep in your heart that your application user experience would benefit a lot from making your tiny tray icon visible by default. This was my case, and as I can see on the internet, this is the case of a lot of apps out there.

I just wanted to write this down as an excercise to help me get back on sharing what I code (not always feasible, though).

There’s plenty of information out there, it’s just you won’t find a single pice of it and you’ll have to digg a lot before being able to workaround this limitation over the tray area. At least this was my experience as a non-experienced Windows developer.

Now that my reasons have been stated clear we can go ahead and see some code.

Let’s get started

So, there’s this incredible resource by Geoff Chappell which you should check if you want to know more about some undocumented/private APIs on Windows. It looks he has done a huge amount of work around the notification area documenting well enough how to workaround the default limitations exposed by the documented API.

As he states here explorer.exe exposes an ITrayNotify implementation through COM. This interface can be used to know user preferences and status regarding the notification area items. And you of course can also use it to modify such preferences.

So what we are going to do now is requesting the implementation through the canonical CoCreateInstance passing in the required CLSID information. Such information is retrievable from the docs by Geoff. But you can also look for ITrayNotify through regedit.exe in order to find the needed CLSID.

Bringing a few pieces together, here is a quick recap of the declarations you’ll need to make the CoCreateInstance call succeed.

[sourcecode lang=”cpp”]

#ifndef __ITrayNotify_INTERFACE_DEFINED__
#define __ITrayNotify_INTERFACE_DEFINED__

class __declspec(uuid("FB852B2C-6BAD-4605-9551-F15F87830935")) ITrayNotify : public IUnknown
{
public:
virtual HRESULT __stdcall
RegisterCallback(INotificationCB* callback) = 0;
virtual HRESULT __stdcall
SetPreference(const NOTIFYITEM* notify_item) = 0;
virtual HRESULT __stdcall EnableAutoTray(BOOL enabled) = 0;
};
#endif // #ifndef __ITrayNotify_INTERFACE_DEFINED__

const CLSID CLSID_TrayNotify = {
0x25DEAD04,
0x1EAC,
0x4911,
{0x9E, 0x3A, 0xAD, 0x0A, 0x4A, 0xB5, 0x60, 0xFD}};

[/sourcecode]

This is enough for requesting the instance through CoCreateInstance. Unfortunately, as I discovered testing my own code, this won’t work on Windows 8 where apparently this private API has changed. You know, this is the drawback of using private APIs :).
Anyway, I spent the day looking for the solution and fortunately I found the appropriate interface also for Windows 8. You can find the same information by running OllyDbg against explorer.exe on Windows 8.

[sourcecode lang=”cpp”]
class __declspec(uuid("D133CE13-3537-48BA-93A7-AFCD5D2053B4")) ITrayNotifyWindows8 : public IUnknown
{
public:
virtual HRESULT __stdcall
RegisterCallback(INotificationCB* callback, unsigned long*) = 0;
virtual HRESULT __stdcall UnregisterCallback(unsigned long*) = 0;
virtual HRESULT __stdcall SetPreference(NOTIFYITEM const*) = 0;
virtual HRESULT __stdcall EnableAutoTray(BOOL) = 0;
virtual HRESULT __stdcall DoAction(BOOL) = 0;
};
[/sourcecode]

Getting the instance to the appropriate ITrayNotify interface, though, is not enough. We are going to use another private interface, called INotificationCB, which will help us get the current information regarding our notification item.

So let’s write down our little helper class that will take care of modifying the preferences for our notification item.

[sourcecode lang=”cpp”]
// TinyTrayHelper.h

class TinyTrayHelper : public INotificationCB
{
public:
TinyTrayHelper(NOTIFYICONDATA* nid);
virtual ~TinyTrayHelper();

HRESULT __stdcall Notify(ULONG, NOTIFYITEM *) __override;

bool ensureTrayItemVisible();

ULONG __stdcall AddRef(void) __override;
ULONG __stdcall Release(void) __override;
HRESULT __stdcall QueryInterface(REFIID riid, void **ppvObject) __override;

private:
NOTIFYICONDATA *_nid;
NOTIFYITEM _nit;
wchar_t _exeName[MAX_PATH];
};
[/sourcecode]

Now let’s see the actual implementation for our helper.

[sourcecode lang=”cpp”]
#include "trayhelper.h"

#include <sdkddkver.h>
#include <VersionHelpers.h>

#include <stdio.h>

static void* CreateTrayNotify(bool win8);

TinyTrayHelper::TinyTrayHelper(NOTIFYICONDATA *nid) :
_nid(nid),
_win8(false)
{
CoInitialize(NULL);

::GetModuleFileName(NULL, _exeName, MAX_PATH);

// here we prepare the NOTIFYITEM instance
// that is required to change settings
_nit.exe_name = _exeName;
_nit.guid = _nid->guidItem;
_nit.hwnd = _nid->hWnd;
_nit.icon = _nid->hIcon;
}

TinyTrayHelper::~TinyTrayHelper()
{
}

HRESULT __stdcall TinyTrayHelper::Notify(ULONG, NOTIFYITEM *item)
{
if (item->hwnd != _nid->hWnd || item->guid != _nid->guidItem) {
// this is a notification about an item that is not ours
// so let’s just ignore it
return S_OK;
}

_nit = NOTIFYITEM(*item);

return S_OK;
}

ULONG __stdcall TinyTrayHelper::AddRef(void)
{
return 1;
}

ULONG __stdcall TinyTrayHelper::Release(void)
{
return 1;
}

HRESULT __stdcall TinyTrayHelper::QueryInterface(REFIID riid, void **ppvObject)
{
if (ppvObject == NULL) return E_POINTER;

if (riid == __uuidof(INotificationCB)) {
*ppvObject = (INotificationCB*)this;
} else if (riid == IID_IUnknown) {
*ppvObject = (IUnknown *) this;
} else {
return E_NOINTERFACE;
}

AddRef();
return S_OK;
}

bool TinyTrayHelper::ensureTrayItemVisible()
{
const bool win8 = IsWindows8OrGreater();
void *trayNotify = CreateTrayNotify();
if (!trayNotify) {
return false;
}

HRESULT hr;
if (win8) {
auto *win8TrayNotify = static_cast<ITrayNotifyWin8*>(trayNotify);
unsigned long callback_id = 0;
// this is synchronous
hr = win8TrayNotify->RegisterCallback(static_cast<INotificationCB*>(this), &callback_id);
hr = win8TrayNotify->UnregisterCallback(&callback_id);
} else {
hr = ((ITrayNotify*)trayNotify)->RegisterCallback(static_cast<INotificationCB*>(this));
hr = ((ITrayNotify*)trayNotify)->RegisterCallback(NULL);
}

if (FAILED(hr)) {
((IUnknown*)trayNotify)->Release();
return false;
}

// now we should have an up-to-date information
// about our notification icon item

if (_nit.preference != 0x01) { // this means always hide, so we honor user preference
_nit.preference = 0x02;

if (_win8) {
((ITrayNotifyWin8*)trayNotify)->SetPreference(&_nit);
} else {
((ITrayNotify*)trayNotify)->SetPreference(&_nit);
}
}
((IUnknown*)trayNotify)->Release();
}

static void* CreateTrayNotify(bool win8)
{
CLSID iTrayNotifyCLSID;
if (win8) {
iTrayNotifyCLSID = __uuidof(ITrayNotifyWindows8); // the interface we defined previously
} else {
iTrayNotifyCLSID = __uuidof(ITrayNotify);
}

void *trayNotify;
HRESULT hr = CoCreateInstance (
CLSID_TrayNotify,
NULL,
CLSCTX_LOCAL_SERVER,
iTrayNotifyCLSID,
(PVOID *) &trayNotify);

if (hr == S_OK) {
return trayNotify;
} else {
printf("Cannot get reference to ITrayNotify instance\n");
}

return NULL;
}

[/sourcecode]

I see what you did there

So, TinyTrayHelper basically does 4 things here:

  1. Creates a NOTIFYITEM instance based on a NOTIFICATIONDATA instance
  2. Chooses the appropriate ITrayNotify instance based on the current OS
  3. Registers itself as an instace of INotificationCB to receive the relevant information inside the Notify method
  4. Finally calls SetPreference to change the preference regarding the notification area item

What now?

What you need now is just to create an instance of our TinyTrayHelper and pass in your NOTIFICATIONDATA instance reference. Then call ensureTrayIconVisible to change the notification area preference regarding your item.

Please note that I adapted a more complex code to build this example so I didn’t test this code specifically. Use at your own risk.

I hope this will be useful to you. Please, let me know if I made tremendous mistakes here, I’ll try to fix!

Cheers.