For the past week, I’ve been working on creating a Single SignOn (SSO) system with ASP.NET. One joke around the office is that SSO could also stand for Seldom SignOn. Essentially, we want to make securing applications as painless for the user as possible. Along the way, being able to generate secure tokens through a Secure Token Service (STS) seemed advantageous as well.
Interestingly, it still comes back to cookies. Tokenizaiton is only useful for securing API’s. That is to say, attaching an Authorization Header to an API call is straight forward, but it’s not feasible to attach one to a user’s initiated browsing.
I’ve created simple SSO systems before with FormsAuthentication, but ASP.NET has changed a lot. Now, we have Windows (ASP.NET) Identity Framework (WIF) and other middleware from Microsoft that attempt to make the process easier. But cookies are, and remain, the main part of the equation. Unless you serve all of your assets anonymously, and then validate the user through JavaScript, you’ll more than likely still need an authentication cookie pushed to the client.
So, the basic system that I’ve devised is using OAuth 2.0. I looked very hard at Thinktecture IdentityServer v3, but ultimately decided to roll my own with Microsoft’s middleware. Thinktecture’s offering is very, very good. However, customization of it had just too many pain points, and once it would have been all said and done, the customizations needed seemed to defeat the purpose of using a bundled piece of software. Also, IdentityServer uses Microsoft’s middleware. I keep mentioning middleware – these are OWIN/Katana. Another very useful library is DotNetOpenAuth.
The basic flow of OpenAuth goes like this:
It took me a long while to wrap my mind around how all of these moving parts integrate. It also took me quite a while to understand what OWIN really does for a developer. OWIN is middleware. This means that it sits in the middle of the ASP.NET pipeline and behaves somewhat like an HttpHandler. It looks for specific requests and handles them. OWIN becomes your STS and handles STS requests on your Authorization Server. The user access your application, the application sends them to the Authorization Server and specifies a redirect callback. The Authorization Server displays a login form, the user logs in, and the Authorization Server redirects the user with a code back to the point you specified. This code can then be used to get an access token.
Another difference in my implementation is that my Authentication Server is not going to use any 3rd party OAuth providers. This eliminates the need for a “grant” phase. Essentially, once the user is authenticated by the Authorization Server, the grant is implicitly made. This is sometimes known as two-legged OAuth.
Generating the access code, access token, and refresh tokens and handling the proper redirects/callbacks are the things that OWIN does for you. OWIN also lets you hook into it to handle things like whitelists for the redirect URL’s and white listing client identifiers. In this particular implementation, we can also globally/universally lock out users and prevent hacking attempts based on the whitelists or x-number of failed login attempts. Of course, logging/storing all of these actions performed by the user is a necessity.
OWIN eliminates a lot of boiler plate implementations and eases the use of OAuth considerably. I also prefer using OAuth for an STS since it uses known methods/protocols for interfacing into the AuthServer. However, all of these things are only part of the picture. In my case, I’m integrating into applications that are not using ASP.NET Identity Framework (WIF). This means that other mechanisms must be in place to initiate the authentication and to wrap the authentication into something that our current crop of apps can consume.
This is where things get interesting.
Call me set in my ways, but I still like FormsAuthentication. It’s simple, it’s easy to create Tickets, encrypt the Tickets, and generate the cookies necessary for indicating that a user is authenticated. This is where DotNetOpenAuth is useful. In order to have the user logged in, the FormsAuthentication Login endpoint initiates the calls to the Authentication Server with DotNetOpenAuth. When the Callback receives the OK from the AuthServer, we can simply pull out some of the Claims that identify the user and wrap these into a FormsTicket/Cookie. For applications that are all on the same domain, these cookie becomes shared.
Using a shared cookie has a nice advantage – destroying that cookie creates a Single SignOff for all applications. It also makes it so that the AuthServer can utilize the same login mechanisms and provide Administration functionality without a lot of work.
Needless to say, this week has been interesting, and I’ve learned a fair bit about more modern web authentication systems.