← Back to Blog
pullmdsecurityoauthauthossMay 7, 2026 · 4 min read

PullMD Part 10: Locking It Down, Properly

API LOCKED · WEB + PWA OPEN → HISTORYAI-generated image
Editorial diagram: a cutaway building with three entrances, labelled API, WEB and PWA. Only the API entrance has a heavy bolted door with a lock, the other two stand open and lead straight to a filing cabinet labelled HISTORY. Above, a dashed line AUTH wraps around the whole building and runs via OAUTH to three user symbols. On the left a panel /authorize with the red finding REVIEW: BLOCKED.Editorial diagram: a cutaway building with three entrances, labelled API, WEB and PWA. Only the API entrance has a heavy bolted door with a lock, the other two stand open and lead straight to a filing cabinet labelled HISTORY. Above, a dashed line AUTH wraps around the whole building and runs via OAUTH to three user symbols. On the left a panel /authorize with the red finding REVIEW: BLOCKED.

The Reddit post from Part 9 had a side effect: from that moment on, quite a lot of people knew there was a service you could call without an account, should anyone ever put it on the internet.

For me that was no problem, because the service only ran on my own network. But now somebody who doesn’t know their way around this could set it up, put it on a rented server, and complain one day. That moved authentication a good way up the list.

Three Commits on the Same Day

So on April 28 three things got added. A token in the configuration that the service checks on every request. Plus two OAuth routes for the cases where a token isn’t enough.

That was the quick answer. A bolt on the door, so there’s some peace for now. But a quick answer carries the risk that you mistake it for a finished one.

My Own Review Said No

The next morning a review ran over the OAuth branch, and it came back with a blocking finding.

The endpoint where a client picks up its permission wasn’t secured itself. On top of that, a list of allowed return addresses was missing. Both together mean: the secret a client is supposed to identify itself with can be walked around.

That’s the uncomfortable part about reviews you order yourself. They find exactly what you didn’t want to find, and they find it before anyone else does.

At that point I could simply have worked through the findings. I didn’t, though.

The Question That Opened It All Up Again

Instead, something else came to me while I was looking at it:

“So I need to think about this properly at some point… what would it look like, assuming we fix all the bugs… I start my Docker and I’ve already got a secret configured in the env and auth turned on… when I open the website, what happens then? When I open the PWA, what happens then?”

Because that’s exactly where the quick answer stopped working. The token protected the interface. The web front end and the app were still standing open. Anyone who knew the address could use the service in a browser, page through the history and see what I read.

One bolt on one of three doors.

So, Properly Then

The answer to my own question came pretty quickly:

“Option 1: that hardly makes sense, if the rest is open, why would I secure MCP at all? Just so I can use it via claude.ai? But then the server has to be reachable from the internet… without protecting the rest? Well, have fun ;-)

Option 2: not complete either, if we do it then do it right! So user/password login on web and PWA, everything that’s an agent via API key, no?

So: lock it down, but properly“

What that meant was clear to me right there. It was a good deal more work than I had planned for that day.

If there’s going to be a login, there have to be users. If there are users, there have to be sessions, passwords and keys.

And then the history doesn’t belong to everyone any more, everyone gets their own. That turns a bolt into a rebuild.

The One Constraint That Decided Everything After It

One point mattered enough to me that I pushed it in the same morning:

“Back to the API key auth for agents, because you just took it as given: would claude.ai access via MCP work with that too, or is OAuth mandatory there? Because I don’t want to block that off for us…”

That was the right question, even if I didn’t like the answer. A key is enough for tools you configure yourself. For the route through the claude.ai web interface it isn’t, and there’s no way around OAuth there.

That settled the order before I had built a single line of it. Users first, then OAuth on top. The other way round doesn’t work, because OAuth without users is pointless.

What came of it is in Part 11. And that OAuth still didn’t work afterwards, for a reason I first suspected somewhere else entirely, is in Part 12 and Part 13.


Part 10 of 15 in the PullMD series.

← PullMD Part 9: Reddit Day and the First Issues | PullMD Part 11: The Multi-User Pivot →

Comments

Commenting takes an email confirmation or a GitHub login. Comments run on my own server, not on an ad provider's — details in the privacy policy.