Windows Authentication + Umbraco Backoffice login results in 401 Unauthorized errors

,

Hi everyone,

I’m troubleshooting an authentication issue with a fresh Umbraco 17 installation and would appreciate some guidance (migrating Umbraco v13 to v17, and v13 does not have this issue).

I’m creating a new Umbraco 17 project and running it with IIS Express using:

"windowsAuthentication": true,
"anonymousAuthentication": false

The database initializes successfully and the Umbraco Backoffice login page loads normally. However, after entering valid Umbraco credentials, the login button briefly shows the successful/checkmark state, then the browser displays the Windows Authentication credential prompt again.

If I cancel that prompt, several Backoffice API requests return 401 Unauthorized and the Backoffice cannot finish logging in.

I’ve also reproduced the same behavior with Umbraco 16.5.1 + .NET 9, so I’m wondering if this is related to the interaction between IIS/Windows Authentication and Umbraco’s Backoffice authentication/OpenIddict rather than something specific to v17.

I’ve opened a GitHub issue with the full reproduction details and screenshots:

https://github.com/umbraco/Umbraco-CMS/issues/23851

I’m also looking into the Umbraco source code to troubleshoot this myself. If anyone has experience with Windows Authentication + Backoffice authentication, I’d particularly appreciate pointers on which part of the authentication pipeline I should investigate.

Thanks!

Hi @anhdo93

Welcome to the forum!

I don’t think this is an Umbraco bug. The v13 backoffice used a cookie, which works fine alongside Windows authentication. From v14 the backoffice sends Authorization: Bearer on every Management API call, so IIS treats the request as unauthenticated and returns its own 401 before Umbraco ever sees it.

If what you’re really after is AD login for the backoffice, use an external login provider over OIDC to Entra ID, which is the supported route now.

I had a look and couldn’t find this reported by anyone else on v14+, and there’s nothing in the docs either. So I’d say it was never supported rather than broken.

It would be interesting to see what HQ reply with in your ticket or if anyone else has tried this approach and come to the same conclusion?

Justin

@justin-nevitech Hi, and thanks for your input!
Just to clarify, I don’t need AD login or any external login provider. I just need to use the built-in Umbraco Backoffice login with users stored in the Umbraco database. Windows Authentication is only enabled because our company requires Anonymous Authentication to be disabled for this intranet site.

From what you’re saying, it sounds like I may need to implement some custom authentication handling to get this working, but I’m not sure if I’m understanding that correctly.

This setup worked fine for us in v13 and earlier, so we definitely weren’t expecting such a change.

If anyone has experience with this setup or knows what I might be missing, I’d really appreciate some help!

Hi @anhdo93

Do you need Windows Authentication on the front-end only? If so, you could switch the backoffice back to Anonymous (it will be protected by the Umbraco login so all anonymous users will see is a login page).

Something like this (AI generated but should work).

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet"
                  arguments=".\MySite.dll"
                  stdoutLogEnabled="false"
                  stdoutLogFile=".\logs\stdout"
                  hostingModel="inprocess" />
      <security>
        <authentication>
          <anonymousAuthentication enabled="false" />
          <windowsAuthentication enabled="true" />
        </authentication>
      </security>
    </system.webServer>
  </location>

  <!-- Backoffice, Management API and the server events hub all live under /umbraco.
       Umbraco handles its own authentication here, so let IIS pass these through. -->
  <location path="umbraco">
    <system.webServer>
      <security>
        <authentication>
          <anonymousAuthentication enabled="true" />
          <windowsAuthentication enabled="false" />
        </authentication>
      </security>
    </system.webServer>
  </location>

  <!-- Only needed if you have packages with backoffice assets -->
  <location path="App_Plugins">
    <system.webServer>
      <security>
        <authentication>
          <anonymousAuthentication enabled="true" />
          <windowsAuthentication enabled="false" />
        </authentication>
      </security>
    </system.webServer>
  </location>
</configuration>

Justin

Thanks again for your response, @justin-nevitech .

Unfortunately, our upper management is not willing to change the current authentication setup for some reason. Personally, I think your approach makes sense, as it allows Umbraco to handle authentication for the backoffice rather than relying on IIS to handle it.

The main issue on my end is that I do not have control over the IIS/server configuration in production. So while this may be a viable approach technically, I don’t have the ability to make the necessary changes on the production servers.

Hopefully, I can find a good solution that works within these constraints.

Hi @anhdo93

I’m just looking into other options for you - I saw you had raised the issue with Umbraco but I don’t know if/when you will get an official response.

Justin

Hi @anhdo93

I’ve created a package to work around this problem if you want to try that?

dotnet add package Umbraco.Community.Security.WindowsAuthentication --version 17.0.0

It moves the Umbraco bearer token into a separate request header so it does not interfere with the Windows Authentication with also uses the bearer token for the Negotiate/NTLM handshake. It then gets restored server side so the backoffice works as normal.

It supports v17 and v18.

Let me know if you have any feedback!

It’s on NuGet, and just waiting for it to appear on the Umbraco Marketplace.

Justin

Well done with the package, Justin!

I am just here to say that the Bearer token is gone entirely in V19, so this workaround becomes obsolete once Umbraco 19 is released. V19 relies solely on cookies, just like V13 did.

Hi @jacob

That’s great to know! Is that change for the same reason, to free up the bearer token or another reason? Just curious why your going back to cookies?

Justin

Umbraco v16.4 and v17.0 reintroduced cookie-based authentication using httpOnly cookies to replace the current localStorage-based JWT token approach:

Since then, the Bearer token has just been a stub value ([redacted]) to initiate OpenIddict’s auth flow.

This migration completes in Umbraco V19, when client-side OAuth/PKCE handling is removed entirely in favor of a pure session-based model (think V13, including reusing the same cookie name).

The thing that kicked this whole endeavour off in 17.0 is this RFC draft, that recommends against storing tokens in a web client:

Thanks for the explanation @jacob !

Thank you so much @justin-nevitech for your amazing effort. Your package worked wonderfully!
Also, thanks @jacob for the heads up with newer versions.

Hi @anhdo93

You’re welcome! Let me know if you have any feedback.

Justin