0 Comments

This is a bug I encountered when working on a web application.

Symptoms

  • You set a cookie in your OWIN middleware, but the cookie is not returned in the response received by a browser.
  • The problem may be intermittent.

Common Scenario

  • You are using an ASP.NET WebForms application with Session.
  • You add CookieAuthenticationMiddleware in the OWIN pipeline.
  • A user tries to login, seems to do so successfully, but the authentication cookie (with default name .ASPNet.Cookies) is not set, so the user is redirected back to the login page.

Cause

The problem is described in this Katana Project post:

In OWIN, the response headers collection is the primary storage location for response cookies. System.Web however stores response cookies in a separate HttpContext.Response.Cookies collection and then writes them out to the Response.Headers collection just before sending the response. This can cause a conflict if OWIN if both approaches are used on the same request, as the Response.Cookies collection will overwrite any cookies set via the OWIN response headers.

For the specific example of working with Session, the Session cookie can be removed during the login process. If this happens on the same request as the authentication cookie is being set in OWIN, the authentication cookie will be missing.

How to Reproduce

The problem only seems to happen when httpResponse.Cookies.Remove is called, as in the following example:

private void SetCookies()
{
    var owinContext = HttpContext.GetOwinContext();
    var owinResponse = owinContext.Response;

    owinResponse.Cookies.Append("owinResponseCookie1", "value1");

    var httpResponse = HttpContext.Response;
    httpResponse.Cookies.Remove("httpResponseCookie1");
}

If you call this SetCookies method from a MVC Controller method, “owinResponseCookie1” will not be set.

Note: If you are just adding a cookie using httpResponse.Cookies.Add, without calling Remove, this does not seem to cause any problems.

Workarounds

1. Use OnSendingHeaders

If you have control of the code setting the cookies in the OWIN middleware, using OnSendingHeaders seems to work.

So in the example below, “owinResponseCookie2” should be set, even though “owinResponseCookie1” is missing.

private void SetCookies()
{
    var owinContext = HttpContext.GetOwinContext();
    var owinResponse = owinContext.Response;

    owinResponse.Cookies.Append("owinResponseCookie1", "value1");

    owinResponse.OnSendingHeaders(state =>
    {
        owinResponse.Cookies.Append("owinResponseCookie2", "value2");
    },
    null);

    var httpResponse = HttpContext.Response;
    httpResponse.Cookies.Remove("httpResponseCookie1");
}

2. Use only HttpResponse.Cookies OR OwinResponse.Cookies

As there is a conflict, the most robust solution is to only set cookies in one place or the other.

If you are using the CookieAuthenticationMiddleware then you can override the CookieManager with an adapter that writes the cookies to HttpResponse instead of using OwinResponse.

The Katana Project post on this problem gives the source code for a SystemWebCookieManager to do this. The manager can be plumbed in as:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
  CookieManager = new SystemWebCookieManager(),

Fix

This problem should be fixed in the next version of Katana targeting ASP.NET Core 1.0 where:

the storage of response cookies has been standardized to always store them in the response header collection.

Post comment