CORS: The cock-blocker

I had never been a fan of Javascript, granted my history with it during the HTML4 days - between browser incompatibilities and quirky behaviours, it had never seemed worthwhile as a language to use despite the promise of a true ‘write-once, run-everywhere’ experience.

But things had improved over the 15 year period, and the combo of HTML5/CSS/Javascript does seem to provide a compelling way of having an easily accessible UI application with very little requirements on where it would work, aside from a browser and an Internet connection.

And so it begins my adventure of writing another Webapp ever since I’ve left university.

In the new-fangled world, plain unchecked HTML just isn’t cool anymore; to be honest, it was a pain to constantly ensure HTML tags are paired, and somewhat crazy that we’ve allowed it to not be ‘compiler-checked’ for so long.

Like what the kool-kids are doing these days, I started with trying to understand react.js, as the way of writing an application. (In fact I started with next.js as I was lazy and wanted to get more easy for free).

I liked what react.js is doing, in the sense that it made development of a UI more akin to what normal languages would do, although at the start, I couldn’t understand why the lunacy of introducing a HTML-clone with jsx, until I mispaired a tag - this thing is a genius in the sense that it finally made HTML a strongly-syntax-checked language!

Stupid problems should be stopped at design-time, not at deployment - I can see why React has finally made web development a non-cowboy like development process.

Why doesn’t my implementation of ‘fetch()’ Javascript work?

It’s also been a long time since I had to pay attention to web security. Obviously, I know that XSS is a problem, but it had always been someone else’s problem to solve, until now.

So imagine my surprise when I what I thought was a simple fetch() call not work?

await fetch(
    'http://127.0.0.1:9080/create',
    {
        method: 'POST',
        headers: {
            'Accept': '*/*',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(formData),
    })
    .then(r => r.text())
    .then(t => {
        console.log("ANSWER" + t)
})

And here’s what I get from the browser:

Took me a while to understand that the browser won’t let me process the request, while it had worked directly where I had my implementation using jquery (on the same service), but rather the issue is with ‘CORS’ (Cross-Origin Resource Sharing).

It took me a while to realise that:

  1. It isn’t because jquery is old and hence is sloppy about security requirements that allowed my code to work;
  2. It isn’t a client-side issue with CORS that can be circumvented by just changing HTTP headers;
  3. The reason why it doesn’t work now is because next.js is served on a separate port (for development for now), outside the service that runs the backend, and this is when CORS kicks in to prevent Cross Domain Scripting Attacks.

The first and second points threw me off a little, as there’s a bunch of ignorant posts on the Internets suggesting various ‘fixes’ that are just cargo-culting without an understanding of what CORS is about. It was infuriating to waste time falling into that trap, and I should have realised that a little sooner.

The funny thing about web-development, is that it seems rather scattered and disparate. Unlike having a full API support set, tutorials, and a somewhat uniform development model, everything is loosely mentioned in some StackOverflow Q&A, which you aren’t really sure if the suggestion still applies over time, or that it is stale and will not work.

Added to the complexity that you can have a combination of frontend+backend, asking a question that fit your matrix is unlikely to yield any fruitful result, unless it’s a popular choice.

Granted that my backend is actix-web, it doesn’t seem like it’s a common combination with node.js at all, so there weren’t any appropriate suggestion that solves my problem specifically.

Thankfully, there is a reference to CORS for actix-web itself, so putting a change temporarily for testing purpose, made it work:

let cors = actix_cors::Cors::permissive();
App::new()
    .wrap(cors)
// ...

This bypasses the XSS security check on actix-web. Do not have this on in production!

Software Development: I’ve just found XXXXX ways that don’t work

Unlike Thomas Edison’s soundbites, there’s just too many ways for programming to not work to wear that statement proudly as a badge.

Life is too short for that.

It’s good to pause and analyse if an approach makes sense after some period of head banging. It’s just sometimes hard to guage how long that period should be.