unable to authenticate using basic authentication

12,172

Solution 1

I see two www-Authenticate response headers. I believe your HTTP module is adding one and IIS is adding one. Ensure all kinds of authentication are disabled in IIS, like so. My guess is that you have basic authentication enabled in IIS.

enter image description here

Solution 2

Just for completeness:

Most examples of BasicAuthentication are describing to enable "Windows Authentication" in the IIS application configuration. This works in many cases (e.g. a browser connecting to the site) but did not work for a dotNet client using network credentials. Why that?

A brief look via fiddler is giving me following http header:

WWW-Authenticate: Basic realm="My Realm"
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM

The browser is choosing BasicAuthentication. But an dotClient is starting the Negotiation (Kerberos) or NTLM authentification.

Case 1: If your server is in the same domain and you are using your normal login credentials => all is fine, the server is doing the negotiation for you

Case 2: If your webapp is providing an own BasicAuthenticationModule for authentication (e.g. has its own user database) the authentication fails.

Solution for case 2: Disable Windows Authentication in the iis or system.webServer-security so that only BasicAuthentication is offered to the client. Then you can use the network credentials to connect to the app without setting the AuthenticationHeader manually or manipulating the authentication cache.

Share:
12,172
Ben
Author by

Ben

Updated on June 14, 2022

Comments

  • Ben
    Ben almost 2 years

    I have an ASP.NET WebApi service that requires http basic authentication (this is for a demonstration, not for production, so that's the reason for basic authentication and not something more secure). The service runs fine with visual studio IIS express server and authentication happens through a custom HTTP module.

    It fails and continues to popup a login screen when I deploy the site to the hosting server. I verified with Fiddler that the request is being sent and the credentials are being sent. But it keeps responding with a 401 unauthorized response. It appears that the request credentials are somehow being lost in the time it takes to get from the client to the server. I have spent many, many hours trying to diagnose this and the .NET authentication with Web API and IIS seems very confusing. Please help!!

    Outgoing request from Fiddler shows:

    GET mywebsiteaddress HTTP/1.1
    Host: my website address
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0
    Accept: /
    Accept-Language: en-US,en;q=0.5
    Accept-Encoding: gzip, deflate
    Authorization: Basic YmJvbm5ldDE4Om9jdG9iZXIxNw==
    X-Requested-With: XMLHttpRequest
    Referer: mysite
    Connection: keep-alive

    Here are the relevant pieces of my config (I can post more if necessary):

    <modules>
      <add name="BasicAuthHttpModule" type="ITMService.Modules.BasicAuthHttpModule"/>
    </modules>
    
    <httpModules>
      <add name="BasicAuthHttpModule" type="ITMService.Modules.BasicAuthHttpModule"/>
    </httpModules>
    
    <authentication mode="Windows"/>
    

    My custom http module (also working fine in testing from visual studio). This was mostly taken from the example on asp.net :

    namespace ITMService.Modules
    {
    public class BasicAuthHttpModule : IHttpModule
    {
    
        private const string Realm = "www.mysite.net";
    
        public void Init(HttpApplication context)
        {
            context.AuthenticateRequest += OnApplicationAuthenticateRequest;
            context.EndRequest += OnApplicationEndRequest;
    
        }
    
        private static void SetPrincipal(IPrincipal principal)
        {
            Thread.CurrentPrincipal = principal;
            if (HttpContext.Current != null)
            {
                HttpContext.Current.User = principal;
                Log.LogIt("current principal: " + principal.Identity.Name);
            }
        }
    
    
        private static bool CheckPassword(string username, string password)
        {
            string passHash = AuthUser.GetUserPassword(username);
            if (PasswordHash.ValidatePassword(password, passHash))
            {
                return true;
            }
            else
            {
                return false;
            }
    
        }
    
        private static bool AuthenticateUser(string credentials)
        {
            bool validated = false;
    
            try
            {
                var encoding = Encoding.GetEncoding("iso-8859-1");
                credentials = encoding.GetString(Convert.FromBase64String(credentials));
    
                int separator = credentials.IndexOf(':');
                string name = credentials.Substring(0, separator);
                string password = credentials.Substring(separator + 1);
    
                validated = CheckPassword(name, password);
    
                if (validated)
                {
                    var identity = new GenericIdentity(name);
                    SetPrincipal(new GenericPrincipal(identity, null));
                }
            }
            catch (FormatException)
            {
                // Credentials were not formatted correctly.
                validated = false;
                Log.LogIt("not validated");
    
            }
            return validated;
        }
    
        private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
        {
    
            var request = HttpContext.Current.Request;
            var authHeader = request.Headers["Authorization"];
            if (authHeader != null)
            {
    
                var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
    
                // RFC 2617 sec 1.2, "scheme" name is case-insensitive
                if (authHeaderVal.Scheme.Equals("basic",
                        StringComparison.OrdinalIgnoreCase) &&
                    authHeaderVal.Parameter != null)
                {
    
                    AuthenticateUser(authHeaderVal.Parameter);
                }
            }
        }
    
        // If the request was unauthorized, add the WWW-Authenticate header 
        // to the response.
        private static void OnApplicationEndRequest(object sender, EventArgs e)
        {
    
            var response = HttpContext.Current.Response;
            if (response.StatusCode == 401)
            {
                response.Headers.Add("WWW-Authenticate",
                    string.Format("Basic realm=\"{0}\"", Realm));
            }
        }
    
        public void Dispose()
        {
        }
    }
    }
    

    My IIS server is hosted and running .NET 4 in integrated pipeline mode. I disabled forms authentication and disabled impersonation. I enabled basic authentication and anonymous authentication methods on the server.

    I've read countless forum responses and posts about this and nothing has lead me to a clear answer.

  • Ben
    Ben almost 11 years
    Yes, it's an authenticate response - The response in fiddler is this: HTTP/1.1 401 Unauthorized Content-Type: text/html Server: Microsoft-IIS/7.5 WWW-Authenticate: Basic realm="www.itmmobile.net" WWW-Authenticate: Basic realm="www.itmmobile.net" X-Powered-By: ASP.NET Date: Tue, 04 Jun 2013 04:31:04 GMT Content-Length: 1293
  • Ben
    Ben almost 11 years
    Thanks very much for your help. Wouldn't disabling them all actually stop the routing of the request to my custom module? Or does that now get passed to the custom module at a different level?
  • Badri
    Badri almost 11 years
    Your module should run just fine. Disable them and see if it works for you. If it works, accept my answer as the answer. That is something people do in StackOverflow, BTW.
  • Ben
    Ben almost 11 years
    I understand the answer thing and I will definitely accept it if it works - I won't be able to test until later today.
  • Ben
    Ben almost 11 years
    My hosting provider locks basic authentication so I can not disable it. I have a ticket in to change that, but not sure when or if it will be disabled for me. Let me know if you have any additional ideas (maybe find a different hosting provider!). Thanks for your answer.
  • Badri
    Badri almost 11 years
    You can add this into your web.config. <system.webServer> <security> <authentication> <anonymousAuthentication enabled="false" /> <basicAuthentication enabled="false" /> <windowsAuthentication enabled="false" /> </authentication> </security> ....
  • Ben
    Ben almost 11 years
    Authentication mode is set to <authentication mode="None" />, should it be set to something different? - there is no option for basic in web.config
  • Badri
    Badri almost 11 years
    None is ok. You will be able to configure basic auth through config when run your app in IIS. Look at the question I have linked in the other answer.
  • Ben
    Ben almost 11 years
    Now I get a 500 internal server error and back on my IIS manager I get the 'This configuration cannot be used at this pat. This happens when the section is locked at a parent level...(overrideModeDefault="Deny")' error. This seems to be a problem with the hosting company not allowing privileges. I have a ticket in and if they change it I will try again.
  • Kris
    Kris about 3 years
    Though this is not exact same issue we're facing, this has helped isolating the issue we had - we got OWIN based authentication, as well bespoke auth, along with Basic auth for Web APIs. Having 'Basic Authentication' on IIS enabled throwing login failures, as it's validated & authenticated using custom filters. Thus, disabled it as pointed out here.