Engineering story · Auth and access plane

The hardening fix that locked everyone out

Reversing my own security change within a day, because the threat model said a different control was the one that mattered

3 min readAuth and access planeOAuth 2.0Dynamic client registration (RFC 7591)
A day of hardening, in order
reviewlocklocked outthreatrevert
The control that protects
YOUa person approves each grant on the consent screen
1 personbetween any client and the tools
  • 3holes the review found; the worst, anonymous registration with attacker-chosen redirects
  • 7acceptance probes, one or more for each fix
  • 24 hfrom adding the registration lock to reverting it

A security review of my deliberately public OAuth endpoint found holes, the worst of them in client registration. I fixed all three, checked the fixes with explicit probes, and the next morning every AI assistant that used the endpoint could no longer authenticate. I re-ran the threat model. The registration lock had added friction without adding security, because the control that protects is the consent screen, where I log in and approve every grant. I reverted my own fix and wrote down why.

The challenge

The endpoint exists so that AI assistants, including commercial ones, can connect to my private tool estate. It has to be public, and the assistants self-register as OAuth clients, some of them caching their client identity indefinitely. I cannot hand a secret to every agent that might connect. The review was right that anonymous registration with free-form redirects is a textbook hole. The question was what closing it bought.

What became of each fix High · anonymous registration with free-form redirects: the lock, reverted the next morningHigh · the same finding, mitigated by the consent stepMedium finding · fix keptLow finding · fix kept
the reviewthe next morningafter

Constraints

Public by design. Self-registering clients that remember their identity, so a lock that invalidates registrations breaks every existing client at once. One operator who cannot be a per-agent secret-distribution service.

My approach

Severity-ordered triage, minimal fixes, explicit probes for each. Then, when the lock broke everything, the discipline to ask the question again from the top: what does an attacker gain from a hostile registration? Nothing, unless a human logs in and approves that client on the consent screen.

A hostile client registers

It holds a client identity and nothing else.

A person decides
YOU

No approval, no access.

The consent screen shows exactly which client is asking for exactly what.

The consent screen is the control. Registration is bookkeeping.

What I built

Admin-gated registration with fail-closed behaviour, plus two smaller fixes for the other findings.

Acceptance probes that checked each fix did what it claimed.

A one-line, reversible bypass of the registration gate, with the rationale written beside it, and the pending-client lifetime raised so legitimate clients stop expiring between uses.

How long a registered client waits for a person
Before1 hourAfter30 days

Difficult decisions

Reverting my own fix. It is uncomfortable to undo a security change the day after making it. It is worse to leave a system nobody can use while believing it is safer.

Choosing consent over registration as the control point. The lock looked like security. The consent step is security.

Not rushing a richer design. The right answer is more nuanced than either the lock or the bypass, and I did not force one in under pressure.

Results

Every client can authenticate again.

Technologies used

OAuth 2.0 · Dynamic client registration · PKCE · Python service · Redis · NGINX front with TLS and rate limits · Threat modelling

What this demonstrates

Security judgment under real use: knowing which control protects, and reversing on evidence rather than defending a change because it was mine.