Building a Single Sign On Provider Using ASP.NET and WCF: Part 2

Using Forms Authentication with WCF

This is the second article in a four part series on building a single sign on () provider using the platform. If you just want to know about forms authentication and and aren’t interested in implementing SSO read ahead. Otherwise, make sure to check out (, , and the source

Introduction

An important distinction to make here before I get started is that I am not using FormsAuthentication to secure a WCF service. I am using FormsAuthentication to manage user identity. FormsAuthentication comes with handy Encrypt/Decrypt methods which allow us to pass a token around between our service, the client and the web application using our service as a trusted identity/authentication provider. But the methods only operate on a FormsAuthenticationTicket instance.

This is really not a problem in this scenario, it gives us just enough of what we need – we can encrypt the data objects we’re passing around and we can also use serialization to include any complex objects in the FormsAuthenticationTicket.UserData property.

Setup

As long as you are hosting WCF from ASP.NET you can take advantage of built-in ASP.NET features, including FormsAuthentication. There are many ways to secure an WCF service, but since we’re trying to build single sign on capabilities and we don’t want our users to be required to obtain additional credentials it makes sense to use FormsAuthentication for our service as well. MSDN contains documentation for setting up FormsAuthentication

  1. Configure ASP.NET FormsAuthentication in the web application where your WCF service is located:
 ...
 
 
 
 ...


This step isn’t required, however it is recommended at least during development. Setting up your dev machine to make your browser think you are communicating with multiple different sites. You need to edit your local “hosts” file, then add host headers to your IIS configuration and then setup Visual Studio to startup each application with the correct url. Then if you have multiple members on your team it further complicates things. If you do it this way, your SSO Service and each application you’re debugging can have a separate cookie path and everything will behave as if you were communicating across domains.

  1. Add to the system.ServiceModel configuration section:
 
 ...


Step 2 gives you access to the ASP.NET intrinsic objects (Context, Server, Session, Request and Response) so you can read/write cookies.

AspNetCompatibility 3. Add the AspNetCompatibilityRequirements attribute to your service implementation class:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class SSOService : ISSOService, ISSOPartnerService
{
 //... concrete interface implementation
}

If you don’t complete step 3, WCF will complain when it sees reads your config settings from step 2 and it doesn’t see the AspNetCompatibilityRequirements attribute on your service implementation.

Using FormsAuthentication

If the client can supply valid credentials then we want to be able to determine the identity of the client for subsequent requests. So after validating the credentials we need to create a ticket and then send it back to the client:

public SSOToken Login(string username, string password)
{
 // default response
 SSOToken token = new SSOToken
 {
 Token = string.Empty,
 Status = "DENIED"
 };
 // authenticate user
 if (string.CompareOrdinal("foo", username) == 0
 && string.CompareOrdinal("bar", password) == 0)
 {
 // mock data to simulate passing around additional data
 Guid temp = Guid.NewGuid();
 // manage cookie lifetime
 DateTime issueDate = DateTime.Now;
 DateTime expireDate = issueDate.AddMonths(1);
 // create the ticket and protect it
 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, issueDate, expireDate, true, temp.ToString());
 string protectedTicket = FormsAuthentication.Encrypt(ticket);
 // save the protected ticket with a cookie
 HttpCookie authorizationCookie = new HttpCookie(FormsAuthentication.FormsCookieName, protectedTicket);
 authorizationCookie.Expires = expireDate;
 // protect the cookie from session hijacking
 authorizationCookie.HttpOnly = true;
 // write the cookie to the response stream
 HttpContext.Current.Response.Cookies.Add(authorizationCookie);
 // update the response to indicate success
 token.Status = "SUCCESS";
 token.Token = protectedTicket;
 }
 return token;
}

Next, when our client needs to assert its identity to a web application using our SSO service we need to provide something which can’t be tampered with and the web application can use to verify with our service to ensure no tampering occurred.

public SSOToken RequestToken()
{
 // default response
 SSOToken token = new SSOToken
 {
 Token = string.Empty,
 Status = "DENIED"
 };
 // verify we've already authenticated the client
 if (HttpContext.Current.Request.IsAuthenticated)
 {
 // get the current identity
 FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
 // we'll send the client its own FormsAuthenticationTicket, but 
 // we'll encrypt it so only we can read it when the partner app
 // needs to validate who the client is through our service
 token.Token = FormsAuthentication.Encrypt(identity.Ticket);
 token.Status = "SUCCESS";
 }
 return token;
}

We’ll use the FormsAuthenticationTicket we created and give the client an encrypted copy. Neither the client nor the web application can read it. The intent is that we give it to the client, the client will forward it to the web application and then the web application will be required ask our service if we can read it and if it is valid. If everything succeeds, we’ll confirm to the web application that the user is indeed who it says it is and can accept our assertion of the client’s identity.

Security

I mentioned this in the last post, but I’ll say it again here. We don’t want to allow session hijacking here and so make sure you set your cookies to HttpOnly

Conclusion

Basically, as long as you configure your service correctly for AspNetCompatibility you can still use forms authentication. By using FormsAuthentication you get everything you need for identity management pre-built for you.

If you’re following the SSO series, the next installment will focus on configuring WCF to support JSONP.