Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting WWW-Authenticate response header within a DelegatingHandler causes havoc on w2k3

I am using a custom authentication mechanism by means of sending a session token. The presence of the token is checked in a DelegatingHandler which sets the current principal accordignly. If the principal is not authorized to call the ApiController method, the controller sends a 401 Unauthorized status. As RFC 2616 requires setting the WWW-Authenticate header always when sending a 401 response, my DelegatingHandler takes care of this.

Now in a scenario with the Web API self-hosted a request which should respond with 401 works correctly on Windows 7, but on Windows Server 2003 it dies with the exception "An existing connection was forcibly closed by the remote host". Further I've noticed that a breakpoint in the controller method gets hit two times on W2k3 in contrast to once in Win7, as if the HttpClient somehow retried the request upon receiving the 401 response.

When I uncomment the line with the WWW-Authenticate header, the program works correctly. Please see the code samples below for a minimalistic reproducing example within a console application.

TestController.cs:

public class TestController : ApiController
{
    public HttpResponseMessage Get()
    {
        return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "go away");
    }
}

AuthenticationHandler.cs:

public class AuthenticationHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>(task =>
        {
            HttpResponseMessage response = task.Result;
            if ( response.StatusCode == HttpStatusCode.Unauthorized &&
                 !response.Headers.Contains("WWW-Authenticate") )
            {
                // comment out this line and the code works
                response.Headers.Add("WWW-Authenticate", "SessionToken");
            }
            return response;
        });
    }
}

Program.cs:

    static void Main(string[] args)
    {
        HttpSelfHostConfiguration config = new HttpSelfHostConfiguration("http://localhost:81");
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}"
        );
        config.MessageHandlers.Add(new AuthenticationHandler());

        using ( HttpSelfHostServer server = new HttpSelfHostServer(config) )
        using ( HttpClient client = new HttpClient() )
        {
            server.OpenAsync().Wait();

            try
            {
                HttpResponseMessage response = client.GetAsync("http://localhost:81/api/test").Result;
                Console.Out.WriteLine(response.StatusCode);
            }
            catch ( AggregateException ex )
            {
                Console.Out.WriteLine(ex.ToString());
            }

            server.CloseAsync().Wait();
        }

        Console.In.ReadLine();
    }

Am I calling the API correctly? Any ideas what could be wrong?

like image 589
metalheart Avatar asked Jun 07 '13 10:06

metalheart


1 Answers

You need to install .net Framework 4.0.3. Prior versions of .net framework could not set the www-authenticate header. Get it here, more info on the issue is in KB2600211.

like image 114
Darrel Miller Avatar answered Nov 02 '22 22:11

Darrel Miller