Got ya when Authing to a Passive STS using Web Forms in Geneva Framework Beta 1

| | Comments (0) | TrackBacks (0)
Lately, I've been doing a lot of work with WS-Trust, WS-Security, WS-Federation, and Information Cards using the Geneva framework and Cardspace Geneva.  It's been really fun and challenging.  Yesterday, however, I ran into a weird issue with the first beta that cost me and three other team members our coffee breaks.

We needed to create a passive STS that allowed users to authenticate using multiple types of credentials.  As our starting point, we used the "Web Application with Multiple Sign In Methods" sample that is included with the Geneva framework SDK, but altered it to use passive redirect.  To do this, we changed the Web.config of the Relying Party (RP) to include the FormsAuthenticationModule (FAM), removed the superfluous ASP.NET Web control from the RP's Web form, and a few other minor tweaks.  (See the claims-aware Web application getting started sample for a complete example of an RP that uses passive redirect.)

After being sent to the STS, the user was presented with a page where they could select their authentication type -- Web forms or info card -- a switchboard if you will.  Each supported type had a button with an event handler that would redirect the user (again) to another page that dealt only with that type of authentication.  The query string was also tacked on before shipping them off. 

In the page that handled Web forms authentication, there was an event handler that fired after logging in.  It redirected the user (dizzying isn't it?) to another page that contained a FederatedPassiveTokenService ASP.NET Web control.  This control is used to surface the custom STS and should do the following:

  1. Detect the presence of a cookie with a certain name and value and,
  2. Redirects the caller back to the RP with the necessary RSTR.
The problem was that it wasn't doing that.  For some reason the cookie wasn't being created, the control didn't find it, and silently did nothing.  The configuration of the IIS application prevented unauthorized access, and sent the user back to the login page.  After submitting the user name and password again, the event handler fired, authentication was successful, and the caller was once again redirected to the page with the FederatedPassiveTokenService control on it.  The second time, however, an error page was displayed saying that access was denied due to the server configuration (error number 401.2).  Of course, the user wasn't allowed to see anything, but they should not have needed to; they should have been sent back to the RP with the cookie.

There in lies the rub.  The cookie wasn't being created.  If it was, the custom STS would have seen it and used the info in the query string to send the authenticated caller on his way.  The workaround: force the cookie to be created explicitly in the event handler of the login form rather than relying on the framework to do it.  This was achieved using this code:

    3 protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)

    4     {

    5         if (FormsAuthentication.Authenticate(Login1.UserName, Login1.Password))

    6         {

    7             string redirectUrl = FormsAuthentication.GetRedirectUrl(Login1.UserName, false);

    8             HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,

    9                 FormsAuthentication.Encrypt(new FormsAuthenticationTicket(Login1.UserName, false, 20)));


   11             Response.Cookies.Add(cookie);

   12             Response.Redirect(redirectUrl + "?" + Request.QueryString);

   13         }

   14     }

Here's the weird thing.  The cookie actually was created sometimes without this explicit code.  We only found the problem after deleting the cookie, so that we could work on logging in using Carspace.  After some tweaking, we got it to create the cookie automatically again.  Then we deleted the cookie, and, as before, subsequent authentication attempts resulted in no cookie and an error.  It wasn't until we used the code above to force the cookie creation that it started to work consistently. The only reason that we could find for this inconsistency was the beta nature of the framework.  This oddity cost four of us our coffee breaks trying to diagnose the problem and work around it.  That's the price you pay for living on the bleeding edge I guess.