A heck of a wild bug chase(georgemauer.net) |
A heck of a wild bug chase(georgemauer.net) |
Having intuition for the foundational layers of our tools saves so much time and future headaches.
There was no form submission, I'm not sure where you got that. There was also no POST. Though yes, I agree that in the core HTTP semantic, you wouldn't want to change state on a GET and that should include not calling `Set-Cookie`. And yet the reality is that that nearly every application - and many popular libraries like auth0 - do in fact set and clear cookies on `GET`.
The issue here was that the `Link` component in NextJs
- does preloading by default (which is a bad idea exactly for the above reason of reality being different from theory)
- doesn't do preloading by default when running on the dev server (so you don't see the error until its deployed)
- because it does preloading directly in javascript, it can't possibly follow the HTTP semantic of not actually applying cookies until later when the cached route is used
Everything else was the wild goose chase bits.
Also I asked claude to criticize the article as a web forum might before publishing, and this is definitely the tone it gave :D
Oh, also, I'm pretty sure I got the part wrong where i was talking about the preload attribute in HTML, but so far no one's noticed. I should correct that.
OP was saying the logout function should have been behind a form submission / POST.
I may be wrong, but I don't think using JavaScript vs using the standard HTML <link> element to prefetch makes a difference here. I don't see anything in the HTML specs about preload or prefetch delaying cookie setting to sometime after the resource is actually loaded (although admittedly I find this bit of the spec somewhat hard to read, as it's dense with references to other parts of the spec). I tried it out, and, both Firefox and Chrome set the cookies for preloaded and prefetched links when the resource is loaded, even if the resource is never actually used.
Come on.
https://www.youtube.com/watch?v=inRB6ull5WQ
(TLDW: allow buttons to make HTTP requests; allow buttons & forms to issue PUT, PATCH & DELETE; allow buttons, forms & links to target elements in the DOM by id instead of only iframes)
would improve the web platform. You could have a stand-alone logout button that issues a DELETE to /session or whatever. Nice and clean.
This is mostly just my personal ramblings, but I'd be curious other peoples viewpoints on this.
One of those magazines told a story about a web site that had lost a lot of data. What had happened? Well, somehow they had this page that
1. Required no authentication at all, and
2. Was using links like
<a href="/path/to/file?action=delete>Delete file</a>
And so the Google web crawler had come across this page and happily visited each and every one of those links.That’s when I learned about the importance of using forms with POST requests for certain actions instead of using links that send GET requests.
And then some years later someone told me about this thing called HATEOAS and about RESTful APIs and that actually there are different HTTP verbs you can use other than just GET and POST. Like for example
DELETE /path/to/file
As for your question about how someone is supposed to learn that these days?Ideally whatever web development tutorials or courses or books they are using would at some point tell them about the different HTTP verbs that exists, and of how and when to use each of them, and crucially to tell them about bad consequences of using GET for anything that has side-effects like logging out a session or deleting a file.
You learn HTML (and see a mention of "POST" method); or read HTTP primer (and see reference to methods) or open browser inspection window and see prominent "Method" column, or see the reference in some other place. You get interested and want to look it up - say wikipedia is often a good start [0] for generic part. And the second sentence of description says it all:
> GET: The GET method requests that the target resource transfer a representation of its state. GET requests should only retrieve data and should have no other effect.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
Also the many HTTP RFCs, this one in particular covers semantics:
https://www.rfc-editor.org/rfc/rfc9110.html
As the age old wisdom says... RTFM :P
HTTP is awesome, I'm in love with it. Beautiful piece of work.
That was where I "learnt" side effects of not using verbs properly. It stuck to me from then.
So instead I used a third party authentication service, store some data in JSON files, and also threw up a lamda gateway to store some more data in Google Sheets?
It's not relevant to the bug hunt, but I'm genuinely intrigued. Is this approach considered easier to work with than using a regular ol' DB?
While this isn't something that _only_ happens in modern javascript, it certainly is a pattern. These bugs are convoluted and difficult to debug because the technologies are convoluted, stacked on top of each other compounding the bugs, and devs do not understand the underlying platform.
Honestly, it's once-again a good case study for why I'd stay from NextJS or "modern JS" devs (despite being in this ecosystem myself, Node/React/RN):
- NextJS, one of the most modern techs in wide use, makes debugging _harder_ than vanilla JS?? This is crazy - Reimplementing browser behaviour in JS is _exactly_ what I would expect to be the root cause of various difficult-to-debug-and-to-fix bugs down the line - Using GET for a logout is a misunderstanding of HTTP semantics. This could have broken in other ways (eg integrating turbolinks in the app).
Well done for debugging and fixing this, but honestly... this doesn't speak to the strength of the technology choices or the author's understanding of the platform
The problem is logging out on a GET request, as discussed in current top comment. It's just semantically incorrect and many tools will (correctly) assume GET requests don't have side-effects (in HTTP's terms, it's a safe method).
E.g. it's very easy to have a GET request cached by mistake (and I've seen some faulty proxies do that, completely ignoring the upstream cache-control).
This is not a problem on Next or its Link component. It's on op's code (and maybe auth0-nextjs allowing logout on GET requests).
It sounds like this could be implemented almost completely without any of that, especially as you were using JavaScript for the data.
All I can hope is that you've learnt a lesson about unnecessary overcomplication.
This seems appropriately relevant today: https://news.ycombinator.com/item?id=41208627
We (the industry) have built up so many layers upon layers and frameworks designed to make things easier that it just seems to attract newcomers to software engineering with this mindset that all it takes is to start with the sample-app for a high level framework, hack on it with trial and error until it does something they want, and then take to social media with proclamations of "Look! I built a thing! You can hire me to build your thing now!"
Also, I didn’t get your ”Claude predicted your tone smiley” thing. OP tone seemed polite and clear. Your tone, on the other hand, seemed defensive and dismissive. Even after you realizing that you initially misunderstood what OP said, adding a “I mean” and a “but I like I said” to reinforce you were right even while misreading what OP said (rather than just acknowledging you got it wrong in the first reading).
I would go even further and speculate that you were predisposed to get a dismissive tone from a web forum (your previous Claude test suggests that) so much that you got a perfectly fine comment and misread in a way that it felt in the “wrong tone” to you. Even misunderstanding what the post said. All of that to confirm your predisposition.
My comment about Claude was simply intended to giggle at how much it has us pegged, not to call out the op directly.
https://www.rfc-editor.org/rfc/rfc7231#section-4.2.1
https://www.rfc-editor.org/rfc/rfc9110#name-safe-methods
By electing to actionably mutate state on GET, one subscribes themselves to a world of hurt.
It is totally how the web works, both as defined by HTTP and in practice. Surely one can pile a dozen workarounds to circumvent the GET safety definition, but then it's just flat out simpler to have it be a POST or DELETE and work as intended.
That a lot of people are doing it a certain - broken - way certainly does not mean they are right.
<button form="logout-form" ...>logout</button>
<form name="logout-form"></form>
No layout implications that way, barring any nth-child css (solvable by putting the form somewhere else). Doesn't solve the form being limited to GET/POST, but styling concerns are atleast handled.It certainly can be, there’s just no _requirement_ that it is idempotent. There’s no problem with having an idempotent POST operation.
I’m also not a fan of a DELETE /session option, the client shouldn’t have to care about the concept of “session”, it’s the server’s problem. But I’m not a fan of resource-based endpoints in general, I tend to prefer task-based endpoint so /logout makes more sense to me.
Also, even if it was possible to trigger DELETE in html it probably would be implemented as a form. Not really a problem, making a form element inline is trivial, and probably needed in various parts of an app (any button that changes some state)
You second paragraph indicates that you do not like the REST pattern of the web, which is fine, but i hope you can appreciate that some of us would like the web as the web to make it possible to abide by that pattern
the last point is addressed in Alex's proposal to allow buttons to function as stand-alone hypermedia controls
Looks like the rough minimum is this:
background: none;
border: none;
padding: 0;
cursor: pointer;
font: inherit;
And, given that some answers there have browser-specific additions, it looks like the spec doesn't have sane effective restrictions on what funky things browsers can do, thus essentially making the answer a "no, you cannot portably make a button look exactly like a link".And no CSS I could find made the button content flow inline with surrounding text (I even went through all of the computed CSS in FF devtools and added the differing ones to the button), so it looks like the answer is "no" in practice too. (though, granted, for a good amount of link-buttons out there you'd probably want no mid-button breaking)
Certainly looks like it'd be simpler to make a <span> act like a link, than a <button>, at least. At which point you lose the implicit accessibility functionality.
Though in my experience, it's great in frameworks like svelte. Define your forms at the top of the component, and you can see at a glance what native actions the component can do, and where it posts to.