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:
- It isn’t because
jquery
is old and hence is sloppy about security requirements that allowed my code to work; - It isn’t a client-side issue with CORS that can be circumvented by just changing HTTP headers;
- 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.