2

I'm trying to make a Login for my Xamarin.Forms App and the Windows Authentication does not work for me, but just from my Xamarin-Code. When I try to browse the Webservice through Postman or just a regular Browser for example I return my result. Before Windows Authentication I used Basic Authentication what worked well and uncomplicated for me.

I have a IIS Server 8.5 where my Webservice runs from.

public async Task<bool> GetLoginAccess(UserCredential userCredentials)
    {
        HttpClientHandler authHandler = new HttpClientHandler()
        {
            PreAuthenticate = true,
            AllowAutoRedirect = true,
            UseDefaultCredentials = true
        };
        using (HttpClient client = new HttpClient(authHandler))
        {
            _client.BaseAddress = new Uri(Properties.Resources.URL_Webservice_Login);
            client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
            var response = await _client.GetAsync("api/LoginChecker?application=***&user=***").ConfigureAwait(false);
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var access = JsonConvert.DeserializeObject<bool>(json);
                return access;
            }
            else
            {
                return false;
            }
        }
    }

Error:

    {StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Date: Tue, 14 Jun 2022 08:21:30 GMT
  Server: Microsoft-IIS/8.5
  WWW-Authenticate: Negotiate
  WWW-Authenticate: NTLM
  X-Android-Received-Millis: 1655194890132
  X-Android-Response-Source: NETWORK 401
  X-Android-Selected-Protocol: http/1.1
  X-Android-Sent-Millis: 1655194890044
  X-Powered-By: ASP.NET
  Content-Length: 1344
  Content-Type: text/html
}}

I don't know what I'm making wrong...

EDIT

So now my Code looks like this:

var credentialsCache = new NetworkCredential(userCredentials.User, userCredentials.Password, "DOM");
        HttpClientHandler authHandler = new HttpClientHandler()
        {
            PreAuthenticate = true,
            Credentials = credentialsCache,
            AllowAutoRedirect = true,
            UseDefaultCredentials = false
        };
        using (HttpClient client = new HttpClient(authHandler, true))
        {
            client.BaseAddress = new Uri(Properties.Resources.URL_Webservice_Login);
            
            var response = await _client.GetAsync("/api/LoginChecker?application=***&user=***");
            if (response.IsSuccessStatusCode)...

And still not working. If im routing to this Page with the Simulator or another physical android device, i get to enter my Credentials, which works fine there, so something in my Code is wrong... :/

EDIT #2

After hours and hours of failures finding my mistake I decided to try WebClient instead of HttpClient. And guess what: IT WORKS GREAT!

Bucks34
  • 21
  • 4

2 Answers2

1

From the IIS documentation:

Windows authentication (formerly named NTLM, and also referred to as Windows NT Challenge/Response authentication) is a secure form of authentication because the user name and password are hashed before being sent across the network. (see here)

I'm not an expert in IIS-topics, neither in NTLM, but I guess in order to do so, you'd have to be logged in as a domain user on the respective device. If you are, the device sends the hashed user credentials to IIS, which in turn authenticates you. Since you are logged in on your machine, your browser and postman are able to send this hashed credentials, get authenticated and are allowed to access whatever you have to access. You're not logged in with your user credentials on the mobile device, hence it is not able to send the credentials and therefor you're neither authenticated, nor authorized, which results in the 401 Unauthorized. Even if the app runs in the simulator on your local machine, it does not know about the credentials, same outcome.

This answer suggests that you'd have to set the HttpClientHandler.Credentials to a CredentialsCache instance (code snipped shamelessly copied) in order to make the authentication/authorization work

var credentialsCache = new CredentialCache
{
    { 
        httpRequestMessage.RequestUri, 
        "NTLM", 
        new NetworkCredential("user", "password", "domain")
    }
};

var handler = new HttpClientHandler { Credentials = credentialsCache };
var client = new HttpClient(handler);
await client.PostAsync(url, data).ConfigureAwait(false);
Paul Kertscher
  • 9,416
  • 5
  • 32
  • 57
  • I did following: HttpClientHandler authHandler = new HttpClientHandler() {PreAuthenticate = true, Credentials = new NetworkCredential(userCredentials.User, userCredentials.Password, "DOMAIN") }; but still not working. Then I tried this. client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("NTLM", Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(userCredentials.User + ":" + userCredentials.Password))); and he creates a hash, but something is still wrong... @Paul Kertscher – Bucks34 Jun 15 '22 at 05:35
0

It seems that the problem is at the client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));.

All of the solutions I found on the SO use the the code client.DefaultRequestHeaders.Add("Key Name", "Key Value");

You can check the following cases:

  1. how to solve Http 401 error,but working in postman,not working in xamarin forms
  2. I am getting StatusCode: 401 "Unauthorized" in a HttpClient.PostAsync (C#)
  3. I'm getting error 401 (unauthorized) while trying to make a GET request from my API that has a JWT token
Liyun Zhang - MSFT
  • 8,271
  • 1
  • 2
  • 14
  • 1
    Thanks for the quick answer, but which key do I type in? – Bucks34 Jun 14 '22 at 09:13
  • Do you have a JWT Token? If so you can check the 3rd link, or you can try to add any two strings.@Bucks34 – Liyun Zhang - MSFT Jun 14 '22 at 09:20
  • No I have no Token, I just want a "real" authentication through ntlm/negotiate. Not within the header. – Bucks34 Jun 14 '22 at 09:27
  • Had you tried to add a header without any change? Such as just add `client.DefaultRequestHeaders.Add("Key Name", "Key Value");`@Bucks34 – Liyun Zhang - MSFT Jun 14 '22 at 09:32
  • If still not work, this may be another conflict. The Api you access need a authentication but your request doesn't have one. So you always get 401. You can try the Paul Kertscher's answer or just add the authentication into the request just like the code in the link I provide.@Bucks34 – Liyun Zhang - MSFT Jun 14 '22 at 09:41
  • Unfortunately it didnt work out, but still thanks for helping @Liyun Zhang - MSFT – Bucks34 Jun 15 '22 at 05:37
  • You are welcome, you can have a try to use a authentication by JWT Token and request header. – Liyun Zhang - MSFT Jun 16 '22 at 09:40
  • I commented this out but still got the error. Obviously a 401 fails before thinking about what the content type is – cgraus Apr 22 '23 at 06:21