Hi Justin,
Great idea, and nicely built. The allowPublicAccess entry point, so the patch installs before routing, is exactly the right hook, and I like that the middleware leaves a request alone when it carries an Authorization header the app may still need.
Two things I would flag, though.
Worth knowing: since 17.0, that isn’t a token. The backoffice sends the literal string Bearer [redacted], and HideBackOfficeTokensHandler swaps in the real access token from the httpOnly umbAccessToken cookie server-side. So what your package relocates is a placeholder, not a credential. Good news for the package’s security profile, and it explains why simply not sending the header doesn’t work: OpenIddict needs something there to start validating.
My actual concern is blast radius, not security. The client script replaces XMLHttpRequest.prototype.open, setRequestHeader, and the status/statusText getters globally, and the server sends 401 as 403 (and 400 on the login POST). That is fine for code that goes through your patch, but everything else on the page sees status codes that no longer mean what they say: logging, monitoring, a WAF, or another package doing its own XHR. Anything that grabbed a reference to fetch before your entry point ran, or lives in a different realm, is unpatched too. You already warn about the pre-install window, which I appreciate.
Could you scope the status rewrite to the Management API paths, or at least call it out prominently in the readme? I think that would take most of the sharp edges off.
Another option, if someone has server-level IIS access, is two zones: keep Windows Authentication on /umbraco and make only /umbraco/management/api anonymous. The backoffice itself stays behind the Windows gate, which I think is what most of these intranet policies are really after, and the browser APIs stay intact. It isn’t free, mind: the token endpoint sits under the Management API, so Umbraco credentials could be tried without a Windows account. Narrower than opening all of /umbraco, but not nothing.
This package came about because of the need for someone who doesn’t have that kind of access to IIS, so it solves their problem.
Either way, it all goes away in v19 