acullen72
acullen72

Reputation: 817

FormsAuthentication cookie not persistent

I'm attempting to save the FormsAuthenticationTicket to a cookie using the following code (in AccountController.cs):

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket
          (1, user.UserEmail, DateTime.Now, 
           DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes), 
           false, null);

string encTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName,
ticket.ToString());
HttpContext.Response.Cookies.Add(faCookie);

if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
           && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
           {
             return Redirect(returnUrl);
           }
           else
          {
             return RedirectToAction("Index", "Home");
          }

As I step through the debugger, everything seems just fine. Until I get to Application_AuthenticateRequest, where I try to retrieve the cookie:

HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

        if (authCookie != null)
        {
         //do stuff here
        }

When I look in the Cookies collection, there's just nothing there. I can add another normal cookie in the AccountController code and it shows up just fine. The problem persists whether or not I include UserData, so I don't think it's a size issue.

Thanks for any insight you can provide.

Upvotes: 2

Views: 3040

Answers (5)

Fred Scales
Fred Scales

Reputation: 11

In your code to write the cookie, you cannot pass a null to the userData parameter with an encrypted cookie. The IsPersistent as false is fine.

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket
          (1, user.UserEmail, DateTime.Now, 
           DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes), 
           false, null);

Do something like this below: In my example, you can replace userData.ToString() with an empty string. Just don't pass it a null! That should fix your problem.

 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
            1,                             // version
            userId.UserEmail,                      // a unique value that identifies the user
            DateTime.Now,                  // created
            DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),  // expires
            false,                    // persistent?
            userData.ToString(),   // user specific data (optional) //NOTE:  DO NOT pass NULL as encrypted string value will become NULL (argh)
            FormsAuthentication.FormsCookiePath  // the path for the cookie
            );

Then in your global.asax.cs You will be checking that cookie in the FormsAuthentication_OnAuthenticate event Your code will vary here as I have written a custom forms authentication and am using a userId rather than email as in your case.

Take note to the following logic that fails if you pass null for your UserData parameter when writing the auth cookie.

    if (authCookie == null || authCookie.Value == "")
    {
        return;
    }

Here is the full event in the globalasax.cs file:

protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs e)
{
    //STEP #1 of Authentication/Authorization flow
    //Reference:  http://msdn.microsoft.com/en-us/library/ff649337.aspx
    //==================================================================
    if (FormsAuthentication.CookiesSupported == true)
    {

        //Look for an existing authorization cookie when challenged via [Authorize]
        HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (authCookie == null || authCookie.Value == "")
        {
            return;
        }
        FormsAuthenticationTicket authTicket = null;
        try
        {
            //Reading from the ticket
            authTicket = FormsAuthentication.Decrypt(authCookie.Value);
            //Check the Cookiename (which in this case is UserId).  If it is null, then we have an issue
            if (authTicket.Name == null)
            {
                FormsAuthentication.SignOut();
                authCookie.Value = null;
            }

        }
        catch (Exception ex)
        {
            //Unable to decrypt the auth ticket
            return;
        }

        //get userId from ticket
        string userId = authTicket.Name;


        Context.User = new GenericPrincipal(
                  new System.Security.Principal.GenericIdentity(userId, "MyCustomAuthTypeName"), authTicket.UserData.Split(','));

        //We are officially 'authenticated' at this point but not neccessarily 'authorized'
    }
    else
    {
        throw new HttpException("Cookieless Forms Authentication is not supported for this application.");

    }
}

Upvotes: 1

Ted Nyberg
Ted Nyberg

Reputation: 7391

Although your authentication ticket is set to be persistent and has an expires date, the actual cookie doesn't.

Add something like this when creating your cookie:

var faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
                   {
                       Expires = ticket.Expiration
                   };

Upvotes: 1

rysama
rysama

Reputation: 1796

You are setting the isPersistent parameter to false when you are creating your FormsAuthenticationTicket. This parameter should be set to true.

Upvotes: 0

John Mc
John Mc

Reputation: 2932

The max size of a cookie is 4096 bytes. If it's above this the cookie wont be saved.

Check the Cookie size using the following:

int cookieSize = System.Text.UTF8Encoding.UTF8.GetByteCount(faCookie.Values.ToString());

Upvotes: 3

Darin Dimitrov
Darin Dimitrov

Reputation: 1038710

You are adding the cookie to the response. Once you have done that make sure you have redirected after immediately. It is only on the subsequent request that you can hope to read it from the Request.Cookies collection.

Upvotes: 1

Related Questions