kick it on DotNetKicks.com   Shout it  

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 (SSO) provider using the ASP.NET platform. If you just want to know about forms authentication and WCF and aren't interested in implementing SSO read ahead. Otherwise, make sure to check out part 1 (part 2, part 3, part 4 and the source are now available).

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 using System.Web.ApplicationService.AuthenticationService. AuthenticationService is designed to allow you to use ASP.NET Membership from any SOAP client regardless of wither or not it uses the .NET Framework. But if you want to use FormsAuthentication and keep it simple just do this:

  1. Configure ASP.NET FormsAuthentication in the web application where your WCF service is located:
    <system.web>
        ...
        <authentication mode="Forms">
            <forms cookieless="UseCookies" path="/SSO" name="SSOService"/>
        </authentication>
        ...
    </system.web>

    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.

  2. Add AspNetCompatibility to the system.ServiceModel configuration section:
    <system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
        ...
    </system.serviceModel>

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

  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 = true.

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.


Feedback

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

Gravatar Great article and I LOVE the reference to Dug the dog from the movie "UP". :) 10/9/2009 12:50 PM | Amy Grossman

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

Gravatar Hi Mark
Thanks for prompt reply.It finally loaded.
But i am unable to debug it to catch the flow of the application.Do i need to make either of the Application1,Application2 as startup page?
Also wat should i enter in login page to move ahead.
Should i used active directory ?
I need to make SSO solution for a distributed web application.Would this application suffice my need?
Regards
Bharat 10/27/2009 5:46 AM | Bharat

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

Gravatar Bharat,

It doesn't matter if Application1 or Application2 is your startup. They are supposed to represent 2 completely separate, distinct applications w/o any dependency upon the other.

The username/password is foo/bar respectively - it isn't hooked up to active directory or any other directory or database. The login is hard-coded in the SSO.Service project in the SSOService.svc.cs class file - look at the Login method. What you use to store credentials is up to you and the requirements of your project. Just replace the hard coded values "foo" and "bar" with values you've retrieved from your chosen credential store.

As for application flow consult the diagram in part 1, here's a link to the image: lh3.ggpht.com/.../SSOFlowDiagram_thumb24.gif

You can use either Fiddler, or if you're using Firefox you can use Firebug to see what happens when you click the login button. The browser will send two requests to the SSO service: 1 to login (SSO.Service.SSOService.svc -> Login method) and another to get the login ticket (SSO.Service.SSOService.svc -> RequestToken method) to be passed to the application. Then a third request is sent to the application where the user is logging in SSO.Application1 (or 2) -> Controllers -> Account.cs -> Authenticate method. The Authenticate method will then make a request to the SSO application to validate the ticket submitted by the client (SSOService.svc.cs -> ValidateToken method). Set your breakpoint in any or each of those 4 methods and use either Fiddler or Firebug and you should be able to follow everything that happens between the client and each application. 10/27/2009 10:25 AM | MarkJMiller

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

Gravatar Hi Mark,
Thanks a lot for explaining it.
I have to make 6 different applications SSO.One of which is java based,another one is a windows application and rest 4 are web applications. These applications will be accessed by different persons sitting at different locations.
Do i need to make any configuration changes?Pls suggest.
Refards
Bharat

10/30/2009 10:33 AM | Bharat

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

Gravatar Mark,
I forgot to mention that those six applications are part of a single project residing on different servers. 10/30/2009 10:39 AM | Bharat

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

Gravatar For the windows client you'll need to make sure your client bindings look like the ones in the web.config for the ASP.NET Applications in the sample solution. The client config will need to point at the endpoint named "partnerEndpoint" so it won't try and use the JSONPBehavior. But the SSO service doesn't need to change. Also, you won't need to go thru the entire flow that a web browser and web application would need to. A desktop client (java or windows) would only need to call the Login operation and check the response for SUCCESS or DENIED. After that there's no need to communicate with the SSO service so you can ignore the ticket, unless you want to get the SSOUser data. In which case you'll need to read the ticket in the response's cookie collection. I"m sure there's an example of how to do that from a windows client out on the web somewhere.

As for the Java client, I haven't tried so you'll have to look around for that one. WCF does support interop with other clients. Off the top of my head it may require a 3rd endpoint at most but other than that you should be fine.

So, long story short if there are any changes to the SSO service they would be minimal - just to support the java client but you'll need to look around for those. And for the windows client at most you'll need to figure out how to read the cookie in the response stream. As for the other 4 web apps there shouldn't be any changes required 10/31/2009 6:57 AM | MarkJMiller

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

Gravatar Hi Mark,
I need to develop SSO solution in framework 2.0 without using WCF or JSONP or MVC

framework.There are two different applications.SSOApp1 and SSOApp2 and a web service which

returns a token.Both applications have simple login page.Upon login in SSOApp1,the user

credentials are verified , token is created and user is logged in.Web Service has

RequestToken method which is same as developed by you. Now i need to Single Sign On SSOApp2.
On the page load event of SSOApp2 ,I need to use the token generated by the web service

while logging in SSOApp1.I am unable to do that.
Kindly suggest.
Regards
Bharat Jain
11/5/2009 9:07 AM | Bharat

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

Gravatar if your applicatons are all members of different domains you need a way to send to the token to the target application. Cookies can only be read by the domain which created them. In order to pass the token to a new domain you need a page from the domain which created the cookie to send it (via POST) to the new domain. Most likely you will need a page on SSOApp1 to provide a means to POST the value of the token to SSOApp2. Place the token in a hidden form field in App1 and do a form POST to App2 to a page which expects the token value. Then App2 can verify the token with the SSO service. If it checks out then it can set a cookie on the client which contains the same token value. Now App2 has access to the token. I hope that helps. 11/5/2009 7:35 PM | MarkJMiller

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

Gravatar Hi Mark,
Can i have your email address?
Please reply me at me_bharatjain@yahoo.com.
Regards
Bharat 11/6/2009 6:41 PM | Bharat

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

Gravatar What would it take to make this service works with php site ?? 11/24/2009 5:13 AM | Car Market for new and used cars

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

Gravatar The client side of things would work exactly the same. However, on the server-side I don't know anything about PHP so you'll have to look at what PHP offers for communicating with web services, specifically WCF 11/24/2009 9:36 AM | MarkJMiller

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

Gravatar Do you have a downloadable working example? 6/14/2010 6:04 AM | DotNetWise

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

Gravatar @DotNetWise, here's the link: www.developmentalmadness.com/samplecode/SSO.zip I thought each of the articles in the series had a link to the source code, but I guess I missed that. 6/22/2010 11:41 AM | MarkJMiller

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

Gravatar Don't you acknowledge that it is high time to receive the home loans, which will make you dreams real. 7/5/2010 12:40 PM | BassCarissa26

Post a comment





 

Please add 7 and 1 and type the answer here:

 

 

Copyright © Mark J. Miller