Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Forms Authentication and Persistant Login Problems


:P
On this page:

I'm mucking around with an authentication form that requires some extra authentication data for the user, so rather than using the stock Login control and it's authenticate method I'm using a custom ticket and manually redirecting the data. I'm not sure how this option to stick custom user data into the ticket escaped me until today, but somehow I missed it over the years <s>. This is obviously quite useful in scenarios where you just need one or two small pieces of user information that you'd otherwise have to store in Session or in an associated user table/membership all of which involves some additional overhead. In two busy applications I'm running this will definitely come in hand and remove the need to run sessions altogether. Duh (sound of hand slapping on forehead <g>).

[updated from Comments - 9/23]

Anyway, here's the code I'm using to handle the Login click from the this manual entry form (ie. no Login Control):

protected void LoginButton_Click(object sender, EventArgs e)
   {
       string username = this.Username.Text;
       string password = this.Password.Text;
       bool remember = this.RememberMe.Checked;
 
       if (UserObject.AuthenticateAndLoad(username,password) != null)
       {
           FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
                   username, DateTime.Now, DateTime.Now.AddDays(10),
                   remember, UserObject.Entity.UserName);
 
           string ticketString = FormsAuthentication.Encrypt(ticket);
           HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticketString);
           if (remember)
               cookie.Expires = DateTime.Now.AddDays(10);
           Response.Cookies.Add(cookie);
 
           Response.Redirect(FormsAuthentication.GetRedirectUrl(username, false));
 
           // *** This works too, although it sends the full page content in addition to Redirect header???
// FormsAuthentication.RedirectFromLoginPage(username,remember);
       }
       else
       {
           this.ErrorDisplay.ShowError("Invalid Login. Please make sure you fill in username and password.");
       }
   }

The authentication seems to work just fine as I get logged into my application, but  for some odd reason the cookie is not created as a persistent cookie when the form is submitted. Just for kicks I also created another form with a LoginControl on it and it too doesn't seem to respect the persistance flag when set on the RememberMe var (or the default control).

Well it turns out I skipped setting hte cookie expires code above which makes perfect sense.

The problem here is that it's confusing of where the expiration is exactly applied. Both the ticket and the GetRedirectUrl() contain the expiration information, and so I assumed that ASP.NET was automatically injecting itself in the middle to set the expiration on the cookie explicitly. Not so - even if you use the explicit RedirectFromLoginPage() - if you call that method directly with the persistCookie option to true, ASP.NET will generate a generic (read very long) expiration time.

In short with the explicit ticket you need to be - well, explicit with the cookie and redirect and handle it all yourself. On future accesses ASP.NET might reissue the ticket and THEN it uses the information provided in the original ticket, but only then not on the original request.

Incidentally I ended up here because I also had problems with the plain old Login control not creating a persistent cookie EVEN IF the Remember me checkbox was applied.

This would not work until explicitly adding the slidingExpiration key:

    <authentication mode="Forms">
      <forms timeout="44000" slidingExpiration="true" ></forms>      
    </authentication>

Thanks to Bilal who pointed at the link in the comments below that pointed at the solution.

Posted in ASP.NET  

The Voices of Reason


 

Rory Christ
September 23, 2007

# re: Forms Authentication and Persistant Login Problems

Rick,

I think your problem is that you aren't setting an expires date/time on the cookie.

HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,ticketString);

cookie.Expires = DateTime.Now.AddDays(10);

Request.Cookies.Add(cookie);


Sure would have been nice if they offered a constructor overload with an expires parameter...

Bilal Haidar [MVP]
September 23, 2007

# re: Forms Authentication and Persistant Login Problems

Hello Rick,
Check this blog post:
http://geekswithblogs.net/vivek/archive/2006/10/13/93956.aspx

I am sure it will solve your problem!

Regards

Rick Strahl
September 23, 2007

# re: Forms Authentication and Persistant Login Problems

@Rory - Duh, thanks. I should stop coding too late at night I guess <g>. Yes that's it. I think my confusion came from the fact that the expiration is provided both on the ticket AND also on GetRedirectUrl() and figured somehow that ASP.NET would use that to inject the expiration policy, but as you point out with the explicit ticket you need to configure the cookie completely.

@Bilal thanks for the link - yes adding the slidingExpiration made the LoginControl work also.

I've updated the post above to reflect both of these comments. Thanks!

Andrei
June 12, 2012

# re: Forms Authentication and Persistant Login Problems

Regarding your last paragraph, i don't understand why you say:
"This would not work until explicitly adding the slidingExpiration key"

slidingExpiration if not specified, is true. therefore it was already set to true in your case.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024