Combine WindowsAuthentication and UseWindowsAzureBearerToken

Oct 12, 2013 at 11:03 AM
My server needs to handle intranet users using WindowsAuthentication and home users via Azure Active Directory. How would I configure this in Startup.cs?

Thanks
Michael
Coordinator
Oct 12, 2013 at 5:08 PM
This could get interesting. WindowsAuthentication is still integrated into the servers, rather than OWIN middleware, so making it cooperate might be tricky.

Step 1: Get two apps working, one with each auth type, just to make sure you know how.
Step 2: Describe your setup for each app (maybe share the config code).
Step 3: Then we can work on merging the two apps.
Oct 13, 2013 at 12:36 PM
Hi Tratcher, I have both versions working.

On the WPF client I choose authentication as follows:-
    abstract public class DataManager
    {
        static DataManager()
        {
            if (Environment.UserDomainName.Equals(ConfigurationManager.AppSettings["ServerDomainName"], StringComparison.CurrentCultureIgnoreCase))
            {
                _useWindowsAuthentication = true;
                _httpHandler = new HttpClientHandler { UseDefaultCredentials = true };
            }
            else
                _authenticationContext = new AuthenticationContext("https://login.windows.net/" + _tenant);
        }

        public static HttpClient GetHttpClient()
        {
            HttpClient httpClient = null;

            if (_useWindowsAuthentication)
            {
                httpClient = new HttpClient(_httpHandler);
            }
            else
            {
                httpClient = new HttpClient();

                AuthenticationResult _authenticationResult = _authenticationContext.AcquireToken(_resource, _clientId, _redirectUri, _userId);

                _userId = String.Empty;

                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", _authenticationResult.CreateAuthorizationHeader());
            }

            httpClient.BaseAddress = _webApiBaseUri;
            return httpClient;
        }
and on the server for WindowsAuthentication I have:-
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            //app.UseWindowsAzureBearerToken(
            //  new WindowsAzureJwtBearerAuthenticationOptions
            //  {
            //      Audience = ConfigurationManager.AppSettings["ida:Audience"],
            //      Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
            //  });

            //app.Use(typeof(ClaimsTransformationMiddleware));

            var config = new HttpConfiguration();
            WebApiConfig.Register(config);

            app.UseDenyAnonymous();

            app.UseWebApi(config);
        }
    }
... and uncomment for AAD authentication.

ClaimsTransformationMiddleware reads the AAD Graph api and transforms groups into roles which are added to ClaimsIdentity. I'll need to do something similar for Windows groups.

My Web.config adds a claimsAuthorizationManager for authorizing access to the web api. Not sure if that is the right way to do it in Katana but it works. I'm currently running the server on IIS but would like to be able to try it out self-hosted as well.

The server is just exposing webapi endpoints; there's no web site or asp.net mvc stuff. I could run two separate servers but would prefer one if it's possible.

Thanks for looking into this.
Michael
Coordinator
Oct 14, 2013 at 4:31 PM
Set IIS to allow Anonymous in addition to Windows Auth, then un-comment UseWindowsAzureBearerToken. With this config DenyAnonymous should send back a 401, and you'll be issued a challenge for Azure (as a redirect?), but I don't think you'll be issued a challenge for WindowsAuth (Negotiate/NTML). Correct? If so then I can show you how to issue a challenge for a specific auth type (Azure or Windows).
Oct 17, 2013 at 2:52 PM
I now have this working on my Dev machine using local IIS Express but run into problems when running on the intranet and targeting Windows Server 2008 R2. I've posted a question here http://social.msdn.microsoft.com/Forums/en-US/9b4e503e-0934-40e1-9761-34515cb22c41/exception-from-httpclient-systeminvalidoperationexception-this-operation-cannot-be-performed-on-a?forum=parallelextensions (not sure if that's the best place) describing the problem.

DenyAnonymous does send back a 401 but also a challenge for WindowsAuth (Negotiate/NTLM). I'm logging the calls and on my dev machine can see an unauthenticated request arriving followed by the same request with AuthenticationType='Negotiate'. Unfortunately, on the intranet this is not playing nicely with the async on the client.

I'm not sure where to head next with this.
Coordinator
Oct 17, 2013 at 4:43 PM
Looks like a known bug in HttpClient. I think it's fixed in .NET 4.5.1 (releasing very soon).

It also looks like you can work around the issue by doing the auth on a HEAD request first.