One tricky little issue that I recently came across in a new codebase was having to extend an API written using ASP.NET Web API 2.2 which was entirely contained within an ASP.NET MVC project. The Web API was configured to use OWIN, the abstraction layer which helps to remove dependencies upon the underlying IIS host, whilst the MVC project was configured to use
System.Web and communicate with IIS directly.

The intention was to use Token-based Http Basic authentication with the Web API controllers and actions, whilst using ASP.NET Membership (Forms Authentication) with the MVC controllers and actions. This is fairly easy to initially hook up, and all authentication within the Web API controllers was implemented via a customized AuthorizationFilterAttribute

Now, the problem with this becomes apparent when a client hits an API endpoint without the relevant authentication header in their HTTP request. Debugging through the code above shows the OnAuthorization method being correctly called and the Response being correctly set to a HTTP Status Code of 401 (Unauthorized), however, watching the request and response via a web debugging tool such as Fiddler shows that we’re actually getting back a 302 response, which is the HTTP Status code for a redirect. The client will then follow this redirect with another request/response cycle, this time getting back a 200 (OK) status with a payload of our MVC Login page HTML. What’s going on?

Well, despite correctly setting our response as a 401 Unauthorized, because we’re running the Web API Controllers from within an MVC project which has Forms Authentication enabled, our response is being captured higher up the pipeline by ASP.NET wherein Forms Authentication is applied. What Forms Authentication does is to trap any 401 Unauthorized response and to change it into a 302 redirect to send the user/client back to the login page. This works well for MVC Web Pages where
attempts by an unauthenticated user to directly navigate to a URL that requires authentication will redirect the browser to a login page, allowing the user to login before being redirected back to the original requested resource. Unfortunately, this doesn’t work so well for a Web API endpoint where we actually wantthe correct 401 Unauthorized response to be sent back to the client without any redirection.

You see, the SuppressFormsAuthenticationRedirect property hangs off the HttpResponse object. The HttpResponse object is part of that System.Web assembly and it’s intimately tied into the underlying ASP.NET / IIS pipeline. Our Web API controllers are all “hosted” on top of OWIN. This, very specifically, divorces all of our code from the underlying server that hosts the Web API. That actionContext.Response above isn't a HttpResponse object, it's a HttpResponseMessage object. The HttpResponseMessage object is used by OWIN as it’s divorced from the underlying HttpContext (which is inherently tied into the underlying hosting platform – IIS)
and as such, doesn’t contain, nor does it have access to a HttpResponse object, or the required SuppressFormsAuthenticationRedirect property that we desperately need!

There are a number of attempted “workarounds” that you could try in order to get access to the HttpContext object from within your OWIN-compliant Web API controller code, such as this one from Hongmei at Microsoft:

Apart from this not working for me, this seems quite nasty and “hacky” at best, relying upon a hard-coded string that references a request “property” that just might contain the good old HttpContext. There’s also other very interesting and useful information contained within a Stack Overflow post that gets closer to the problem, although the suggestions to configure the IAppBuilder to use Cookie Authentication and then to perform your own login in the OnApplyRedirect event will only work in
specific situations, namely when you’re using the newer ASP.NET Identity, which itself, like OWIN, was designed to be disconnected from the underlying System.Web / IIS host. Unfortunately, in my case, the MVC pages were still using the older ASP.NET Membership system, rather than the newer ASP.NET Identity.

So, how do we get around this?

Well, the answer lies within the setup and configuration of OWIN itself. OWIN allows you to configure and plug-in specific “middleware” within the OWIN pipeline. This allows all requests and responses within the OWIN pipeline to be inspected and modified by the middleware components. It was this middleware that was being configured within the Stack Overflow suggestion of using the app.UseCookieAuthentication. In our case, however, we simply want to inject some arbitrary code into the OWIN pipeline to be executed on every OWIN request/response cycle.

Since all of our code to setup OWIN for the Web API is running within an MVC project, we do have access to theSystem.Web assembly’s objects. Therefore, the fix becomes the simple case of ensuring that our OWIN configuration contains a call to a piece of middleware that wraps a Func<T> that merely sets the required
SuppressFormsAuthenticationRedirect property to true for every OWIN request/response:

// Configure WebAPI / OWIN to suppress the Forms Authentication redirect when we send a 401 Unauthorized response
// back from a web API. As we're hosting out Web API inside an MVC project with Forms Auth enabled, without this,
// the 401 Response would be captured by the Forms Auth processing and changed into a 302 redirect with a payload
// for the Login Page. This code implements some OWIN middleware that explicitly prevents that from happening.
app.Use((context, next) =>
{
HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true;
return next.Invoke();
});

And that’s it!

Because this code is executed from the Startup class that is bootstrapped when the application starts, we can reference the HttpContext object and ensure that OWIN calls execute our middleware, which is now running in the context of the wider application and thus has access to the HttpContext object of the MVC projects
hosting environment, which now allows us to set the all-important SuppressFormsAuthenticationRedirect property!