Skip to main content

If you run Umbraco on an intranet, there's a fair chance IIS is set up with Windows Authentication on and anonymous authentication off. That worked fine up to Umbraco 13. From Umbraco 14 onwards the backoffice stops working in that setup, and there's no obvious way round it. I've released a small package that sorts it out for Umbraco 17 and 18.

Where this started

It came out of a question on the Umbraco forum. A company was moving an intranet site from Umbraco 13 to 17. Their policy requires anonymous authentication to be disabled, and they had no control over the IIS configuration on their production servers. On the new site the login page loaded, but after signing in the browser kept asking for Windows credentials and the backoffice never finished loading.

The usual advice would be to let IIS allow anonymous access to /umbraco and leave Umbraco to handle its own login. That wasn't an option for them, so I looked at whether it could be fixed from inside the site instead.

Why it stopped working

Windows Authentication in IIS uses the Authorization header for its Negotiate and NTLM handshake. The Umbraco 13 backoffice used a cookie, so it never needed that header and the two got on fine together.

The newer backoffice talks to the Management API, and every one of those requests carries its own Authorization: Bearer header. In recent versions the real token has moved into a secure cookie, but the header is still sent. IIS sees an Authorization header it can't use, treats the request as unauthenticated and returns a 401 before Umbraco ever gets a look at it.

There's a second, less obvious problem. IIS adds its Windows challenge to every 401 the site returns. So when your backoffice session ends, instead of Umbraco's re-login you get the browser asking for Windows credentials.

How the package works

There are two small parts, one in the browser and one on the server. Neither needs any changes to Umbraco itself or to any other packages you have installed.

In the browser

The package registers an app entry point, which Umbraco loads before the backoffice sends its first authenticated request. It wraps the browser's fetch and XMLHttpRequest, and for requests to the backoffice it moves the bearer token out of the Authorization header and into a header of its own, X-Umb-Authorization. IIS never sees a bearer token, so its Windows Authentication completes as normal. Core Management API calls, package API clients, uploads and SignalR all go through those same browser APIs, so they're all covered.

On the server

A composer adds middleware to the very start of Umbraco's request pipeline. It copies the value from X-Umb-Authorization back into the Authorization header before routing and authentication run, so OpenIddict and every protected controller see exactly the request they would have seen without the package.

Keeping Umbraco's own login messages

To stop the Windows prompt appearing when a session ends, the middleware sends a 401 on those backoffice requests as a 403 with a marker header. IIS leaves a 403 alone, and the script in the browser turns it back into the original 401, so Umbraco shows its normal re-login. A failed sign-in on the login page gets similar treatment. The login page doesn't load the script, so that 401 goes out as a 400 instead, which the login page already handles in the same way. You see Umbraco's "couldn't log you in" message rather than a Windows prompt.

What it doesn't touch

The middleware only acts on requests carrying the X-Umb-Authorization header, and only the backoffice script sends that. Front-end pages, member logins, public access, the Delivery API and any custom authentication you have, such as JWT, API keys or Basic, are left exactly as they were. It's also harmless on a site without Windows Authentication, for example Kestrel in local development. The tests check all of this under IIS Express with Windows Authentication and under Kestrel, on both Umbraco 17 and 18.

Installing it

The package supports Umbraco 17.5 or later and Umbraco 18. Packaging is version-aligned, so the package major matches your Umbraco major. It's worth pinning the major when you install, as NuGet won't pick the matching one for you:

dotnet add package Umbraco.Community.Security.WindowsAuthentication --version "17.*"

dotnet add package Umbraco.Community.Security.WindowsAuthentication --version "18.*"

You'll find it on NuGet and the Umbraco Marketplace, and the source code and full documentation are on GitHub.

Setting up IIS and browsers

There's nothing to configure in Umbraco. In IIS, turn off anonymous authentication and turn on Windows Authentication for the site. Browsers also need to trust the site before they'll sign you in with your Windows account automatically. For Edge and Chrome that means adding it to the Local Intranet zone or the AuthServerAllowlist policy. Firefox ignores the intranet zone and needs its own Authentication policy, otherwise it will keep asking for credentials.

What about Umbraco 19?

Jacob from Umbraco HQ replied on the forum thread to say the bearer token is gone entirely in Umbraco 19, which relies on cookies just like Umbraco 13 did. That's good news, and it means this package won't be needed once you're on 19. It's part of a wider move to stop exposing tokens to the browser, which started with moving the backoffice tokens into secure cookies in Umbraco 16.4 and 17.

Until then, if you're on 17 or 18 with Windows Authentication and no say over the IIS setup, this should get your backoffice working again. If you give it a try, let me know how you get on, or raise an issue on GitHub.