Catching the System.Web/Owin Cookie Monster

CookieMonster-SittingCookies set through the Owin API sometimes mysteriously disappear. The problem is that deep within System.Web, there has been a cookie monster sleeping since the dawn of time (well, at least since .NET and System.Web was released). The monster has been sleeping for all this time, but now, with the new times arriving with Owin, the monster is awake. Being starved from the long sleep, it eats cookies set through the Owin API for breakfast. Even if the cookies are properly set, they are eaten by the monster before the Set-Cookie headers are sent out to the client browser. This typically results in heisenbugs affecting sign in and sign out functionality.

TL;DR

The problem is that System.Web has its own master source of cookie information and that isn’t the Set-Cookie header. Owin only knows about the Set-Cookie header. A workaround is to make sure that any cookies set by Owin are also set in the HttpContext.Current.Response.Cookies collection.

This is exactly what my Kentor.OwinCookieSaver middleware does. It should be added in to the Owin pipeline (typically in Startup.Auth.cs), before any middleware that handles cookies.

app.UseKentorOwinCookieSaver();

The cookie saver middleware preserves cookies set by other middleware. Unfortunately it is not reliable for cookies set by the application code (such as in MVC Actions). The reason is that the System.Web cookie handling code might be run after the application code, but before the middleware. For cookies set by the application code, the workaround by storing a dummy value in the sessions is more safe.

Using Owin External Login without ASP.NET Identity

ASP.NET MVC5 has excellent support for external social login providers (Google, Facebook, Twitter) integrating with the ASP.NET Identity system. But what if we want to use external logins directly without going through ASP.NET Identity? Using external logins together with ASP.NET Identity is very simple to get started with, but it requires all users to register with the application. External logins are just another authentication method against the internal ASP.NET Identity user. In some cases there is no need for that internal database, it would be better to get rid of it and use the external login providers without ASP.NET Identity. That’s possible, but requires a bit of manual coding.

For public facing web applications I think that it is often a good idea to use ASP.NET Identity as it doesn’t tie the user to a specific login provider. But if we are fine with using one and only one specific login provider for each user it’s possible to skip ASP.NET Identity. It could be an organization that heavily relies on Google Apps already so that all users are known to have Google accounts. It could be an application that uses SAML2 based federative login through Kentor.AuthServices.

In this post I’ll start with a freshly created ASP.NET MVC Application without any authentication at all and make it use Google authentication, without ASP.NET Identity being involved at all. The complete code is available on my GitHub account.

Beware of Uri.ToString()

When working with urls, it’s sometimes better to use the Uri class than to keep the Uri in a simple string. The Uri class helps validate that the format is a valid Uri and helps splitting out the parts of the Uri in a safe manner. But there is a big gotcha in that Uri.ToString() returns an unescaped representation of the Uri.

The contents of this post might sound simple, but they were behind a nasty heisenbug. Every single insight in this post is something that I learned in a very painful way. I hope that reading this post will convey the same insights in a less painful way.

TL;DR; in two lines of code

The entire problem can be expressed in two lines of code.

var uri = new Uri("http://localhost?p1=Value&p2=A%20B%26p3%3DFooled!");
Console.WriteLine("uri.ToString(): " + uri.ToString());

It looks simple and it should be simple, but it isn’t. When running these two lines on the .NET Framework 4 the following output is produced:

http://localhost/?p1=Value&p2=A B&p3=Fooled!

The query string has been decoded in such a way that it looks like there is an extra parameter p3!

When targeting .NET 4.5 however only the space is unescaped. This can be explained as a result of the breaking changes to System.Uri in .NET 4.5. But that is not the whole story. It gets more complicated (and bug prone) because .NET 4.5 is an in place upgrade to .NET 4.0.

Kentor.AuthServices SAML2 Owin Middleware Released

I just pushed the first version of our Owin SAML2 middleware to nuget and github as part of Kentor.AuthServices 0.5.2. Kentor.AuthServices is a SAML2 Service Provider implementation for ASP.NET, offering an HTTP Module, drop in MVC controllers and (now) an Owin middleware. The intention with the library is to approach SAML2 from a .NET perspective, making a component that fits seamlessly into the existing .NET security infrastructure.

Kentor.AuthServices is now made up of four main packages.

  • The core Kentor.AuthServices library which contains all the SAML2 functionality and the classic http module. The library exposes a public API for working with SAML2 authentication requests and responses, that can be used to e.g. build an identity provider.
  • The Kentor.AuthServices.Mvc library which is a drop in MVC controller that enables SAML2 authentication to an MVC application without writing a single line of code. Just install the nuget package and add the needed configuration to web.config to get going.
  • The Owin middleware that offers SAML2 authentication in the same way as existing providers for external authentication (e.g. Google, Facebook) and integrates with ASP.NET Identity.
  • The Stub idp for testing, that is available for free and answers to any incoming AuthnRequests.

All the source is hosted in a GitHub repo, including sample applications.

What’s new in 0.5.2?

There are three major improvements in 0.5.2 over previous versions.

  • Basic Service Provider metadata with the data that is mandatory according to the SAML2 specification is generated and published.
  • There is now support for one instance of AuthServices to work with multiple identity providers. This is a first step on the road to full federation support.
  • We’ve created a brand new owin middleware that is compatible with ASP.NET Identity and works the same way as existing external authentication providers such as Google and Facebook.

For the full list of implemented features, please see the GitHub Milestone for 0.5.0

Writing an Owin Authentication Middleware

Owin and Katana offers a flexible pipeline for external authentication with existing providers for authentication by Google, Facebook, Twitter and more. It is also possible to write your own custom authentication provider and get full integration with the Owin external authentication pipeline and ASP.NET Identity.

Anatomy of an Owin Authentication Middleware

For this post I’ve created a dummy authentication middleware that interacts properly with the authentication pipeline, but always returns the same user name. From now on I will use the names from that dummy for the different classes.

A typical Katana middleware is made up of 5 classes.

  1. The main DummyAuthenticationMiddleware class.
  2. The internal DummyAuthenticationHandler class doing the actual work.
  3. A DummyAuthenticationOptions class for handling settings.
  4. An extension method in DummyAuthenticationExtensions for easy setup of the middleware by the client application.
  5. An simple internal Constants class holding constants for the middleware.
Software Development is a Job – Coding is a Passion

I'm Anders Abel, an independent systems architect and developer in Stockholm, Sweden.

profile for Anders Abel at Stack Overflow, Q&A for professional and enthusiast programmers

Code for most posts is available on my GitHub account.

Popular Posts

Archives

Series

Powered by WordPress with the Passion for Coding theme.