AzureAD / microsoft-authentication-library-for-dotnet

Microsoft Authentication Library (MSAL) for .NET
https://aka.ms/msal-net
MIT License
1.39k stars 341 forks source link

[Bug] Better error reporting when IWA is failing is needed #1825

Closed jabbera closed 4 years ago

jabbera commented 4 years ago

Which Version of MSAL are you using ? 4.13.0

Platform net462

What authentication flow has the issue?

Other? - please describe;

Is this a new or existing app? This is a test app

Repro

using Microsoft.Identity.Client;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp12
{
    class Program
    {
        private const string CLIENT_ID = "<CLIENTID>"; // MAKE THIS YOURS
        private const string TENANT_ID = "<TENANTID>";
        private static IPublicClientApplication clientApplication; // Thread safe;

        static async Task Main(string[] args)
        {
            try
            {
                clientApplication = await CreatePublicClientWithCacheAsync();

                var token = await GetToken().ConfigureAwait(false);
                Console.WriteLine(token);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            finally
            {
                Console.ReadLine();
            }
        }

        private static async Task<string> GetToken()
        {
            string[] scopes = new string[] { "https://database.windows.net//user_impersonation" };
            AuthenticationResult result;
            try
            {
                result = await clientApplication.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync();
            }
            catch (MsalUiRequiredException)
            {
                result = await clientApplication.AcquireTokenInteractive(scopes).ExecuteAsync();
            }

            return result.AccessToken;
        }

        private static async Task<IPublicClientApplication> CreatePublicClientWithCacheAsync()
        {
            IPublicClientApplication pca = await CreatePublicClientAndBindCacheAsync()
                .ConfigureAwait(false);

            return pca;
        }

        private static async Task<IPublicClientApplication> CreatePublicClientAndBindCacheAsync()
        {

            var appBuilder = PublicClientApplicationBuilder.Create(CLIENT_ID)
                .WithTenantId(TENANT_ID)
                .WithHttpClientFactory(new StaticClientWithProxyFactory());

            var app = appBuilder.Build();
            Console.WriteLine($"Built public client");

            return app;
        }
        // *************************** IGNORE THIS ***************************
        public class StaticClientWithProxyFactory : IMsalHttpClientFactory
        {
            private static readonly HttpClient s_httpClient;

            static StaticClientWithProxyFactory()
            {

                s_httpClient = new HttpClient(new LoggingHandler(new HttpClientHandler()));

            }

            public HttpClient GetHttpClient()
            {
                return s_httpClient;
            }
        }

        public class LoggingHandler : DelegatingHandler
        {
            public LoggingHandler(HttpMessageHandler innerHandler)
                : base(innerHandler)
            {
            }

            protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                Console.WriteLine("Request Headers:");
                foreach(var header in request?.Headers)
                {
                    Console.WriteLine($"{header.Key}: {header.Value}");
                }

                Console.WriteLine("Request:");
                Console.WriteLine(request.ToString());
                if (request.Content != null)
                {
                    Console.WriteLine(await request.Content.ReadAsStringAsync());
                }
                Console.WriteLine();

                HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

                Console.WriteLine("Response:");
                Console.WriteLine(response.ToString());
                if (response.Content != null)
                {
                    Console.WriteLine(await response.Content.ReadAsStringAsync());
                }
                Console.WriteLine();

                return response;
            }
        }
        // *************************** IGNORE THIS ***************************
    }
}

Expected behavior A clear and concise description of what you expected to happen (or code). Seamless SSO works.

Actual behavior Exception is thrown even without my logging handler:

{"Federated service at https://autologon.microsoftazuread-sso.com/SNIP/winauth/trust/2005/windowstransport?client-request-id= returned error: "}

For some reason https://autologon.microsoftazuread-sso.com//winauth/trust/2005/windowstransport?client-request-id= is returning a 401 and the request is not attaching my kerberos token or resending witht he token after a 401. SSMS and chrome are working fine with seamless SSO.

Request with headers:

Method: POST, RequestUri: 'https://autologon.microsoftazuread-sso.com/<SNIP>/winauth/trust/2005/windowstransport?client-request-id=<SNIP>', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:       {                                                                                                                         ContentType: application/soap+xml                                                                                       SOAPAction: http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue                                                       Content-Type: application/soap+xml; charset=utf-8                                                                     }                                                                                                                       

Response with headers:

StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Pragma: no-cache
  Vary: Origin
  X-Content-Type-Options: nosniff
  Access-Control-Allow-Origin: https://login.microsoftonline.com
  Access-Control-Allow-Credentials: true
  Access-Control-Allow-Methods: GET
  x-ms-request-id: <SNIP>
  x-ms-ests-server: 2.1.10519.16 - CHI ProdSlices
  Cache-Control: no-store, no-cache
  P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
  Set-Cookie: <SNIP>
  Set-Cookie: <SNIP>
  Set-Cookie: <SNIP>
  WWW-Authenticate: Negotiate
  Date: Fri, 15 May 2020 14:39:19 GMT
  Content-Length: 0
  Expires: -1
}
henrik-me commented 4 years ago

@trwalke : hoping you can review/investigate this issue.

jabbera commented 4 years ago

Never mind.

What a hot mess this issue is but turns out it's on our firewall. Will update more when I have details.

jabbera commented 4 years ago

Turns our our firewall was intercepting the repsonse

bgavrilMS commented 4 years ago

Reopening the issue so that we can capture the supportability fix that you've done in the release notes @jabbera

trwalke commented 4 years ago

Resolved in 4.15