7

My project has problem renewing Forms Authentication Session due to conflicting Forms Auth Cookies.

Detailed Description:

After a user logged in, one forms auth cookie (FACookieA) is created, and s/he is authenticated. When it comes to renew the cookie, however, a second forms auth cookie (FACookieB) is created, and FACookieA is not renewed. The User is redirected to login page on page request after the expiration time in FACookieA, even it is before expiration time in FACookieB.

Generated cookies:

Please note that both cookies have the same name.

FACookieA:

name: FormsAuth
domain: .formsauth.com

please note the "." pre-appended by .NET, the "formsauth.com" is from Forms Authentication Ticket section

FACookieB:

name: FormsAuth
host: a.formsauth.com

please note the cookie uses "host", not domain, and "a.formsauth.com" is based on the current request url domain.

Project url tested:

a.formsauth.com

Web.config:

<forms loginUrl="~/Account/Login.aspx" name="FormsAuth"/>

Code

public partial class Account_Login : System.Web.UI.Page
{   
    protected void LoginButton_Click(object sender, EventArgs e)
    {
        if (Membership.ValidateUser(LoginUser.UserName.Trim(), LoginUser.Password.Trim()))
        {
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                 1,
                "username",
              DateTime.Now,
              DateTime.Now.AddMinutes(2),
              false,
              string.Empty
              );

            string encryptedTicket = FormsAuthentication.Encrypt(ticket);
            HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
            cookie.Domain = "formsauth.com";
            cookie.Path = FormsAuthentication.FormsCookiePath;
            Response.Cookies.Remove(cookie.Name);
            Response.Cookies.Add(cookie);
            Response.Redirect("~/Account/ChangePassword.aspx"); //authenticated area

        }else
        {
            Response.Write("Invalid UserID and Password");
        }
    }
}

Questions:

1) How to generate one forms auth cookie, so that users can renew forms auth session and will not be logged out?

Considerations:

1) The project needs to support multiple languages, with possible domain formats below:

a.formsauth.com
a.en.formsauth.com
a.us.formsauth.com

and

b.formsauth.com
b.en.formsauth.com
b.us.formsauth.com

Thus, I cannot set the domain attribute of Forms element declaratively. Because two sets of domain cannot share cookie, sharing cookie within one set is allowed. That is the same code base is used for different apps with different domains. But one set of domain can share cookie.

2) The default built-in FormsAuthenticationModule renews user session cookie, which is why I has no control over the domain in the cookie. Please note that FormsAuthenticationTicket is used to create cookie upon use login as shown above.

Any idea?

Pingpong
  • 7,681
  • 21
  • 83
  • 209
  • Please post the code that refreshes the cookie – Mike Apr 27 '15 at 20:22
  • did you set `cookie.Domain = "formsauth.com";` when you refresh the cookie? – Khanh TO Apr 28 '15 at 02:08
  • I don't remember ASP.NET Forms Auth too well, so this may not work with your implementation at all, but have you tried adding `slidingExpiration="true" cookieless="UseCookies" timeout="x"` into `` in your web.config? `x` is the time in minutes of inactivity before the user has to login again. – JW Lim Apr 28 '15 at 05:06
  • Have you got both domains mapped to a single web application or maybe you are running two different web applications? Asking because by default IIS 7 (and IIS 8 as well if memory serves) generates the unique encryption key per application at runtime. Meaning that the 2nd app would not be able to decrypt the cookie from the first app. In this case you will see errors related to decryption in the EventLog. PS. Generation at runtime means that the key is regenerated whenever app pool restarts - which may also cause issues sometimes. – anikiforov Apr 28 '15 at 20:51
  • 1
    Also, you may want to change your Web.config to: this is to ensure that the default FormsAuthenticationModule will be generating cookies with the same domain instead of the null domain. E.g. FormsAuthenticationModule generates the expired cookie and pushes it to browser. If domain is not set (null), you will be leaving the choice of domain up to browsers. [Which may be troublesome](http://stackoverflow.com/questions/10751813/cookies-with-and-without-the-domain-specified-browser-inconsistency) – anikiforov Apr 28 '15 at 21:27
  • @anikiforov Sorry for the confusion, I updated the Consideration section. Please have a look. – Pingpong Apr 29 '15 at 09:09
  • After looking at your update - I would try the following: 1) create a website in IIS with two web apps in it: A and B. App A will serve a.formsauth.com and its subdomains, app B will serve b.formsauth.com and its subdomains. 2) both would have domain="formsauth.com" set in the element in web.config 3) set cookiePath - /A and /B respectively - this will scope your cookies to the respective applications (you can also use element for this). You will deploy two similar instances of your web app, but the config will be different. – anikiforov Apr 29 '15 at 12:32
  • @anikiforov on 3, do you mean cookie path: / for A, and / for B? I know it works but it requires separate apps in IIS. – Pingpong Apr 29 '15 at 22:34
  • /A for A and /B for B - if you want to use the same cookie name. Yes, it requires two separate web apps under a single web site. – anikiforov Apr 29 '15 at 22:50
  • @anikiforov After re-read your steps, I have questions: 1) because we create a website in IIS, and two web apps under it, how to define the binding for the website? Because the url of any web app under a website would look like this: websiteDomain/WebAppName. Please correct me if I am wrong. – Pingpong Apr 30 '15 at 21:16
  • yes, you are right - formsauth.com/A and formsauth/B. You could then use IIS url rewrite to ensure that requests to a.formsauth.com actually go to formsauth.com/A. Alternatively, instead of having website + 2 apps, you can simply try setting up two web sites running on the same port and using host headers in site binding to route requests, more details [here](http://stackoverflow.com/a/8903357/2112891) and [here](http://www.dotnetscraps.com/dotnetscraps/post/Did-you-know-Add-host-header-to-a-Web-Site-in-IIS-7-IIS-75.aspx) – anikiforov Apr 30 '15 at 22:08
  • @anikiforov, Another option is to create two websites, first has biding: a.formsauth.com, and its subdomains, and second is b.formsauth.com and its subdomains. It is easier. Setting cookiePath to "/" is enough, which is the default. – Pingpong May 01 '15 at 15:18
  • Exactly. And then either leave domain attribute in the config empty, or set it to *a.formsauth.com* on for the 1st website and *b.formsauth.com* for the 2nd one. You don't want to be setting domain to *formsauth.com* in the config or in the code since you do not want cookies shared between these two websites. – anikiforov May 01 '15 at 15:27
  • @anikiforov I know that works, but it requires overheads on setup. Thus, I posted this thread for better solution. Thank you for your advice. – Pingpong May 02 '15 at 22:44
  • I attached an event handler to the HttpApplication.PreSendRequestHeaders event instead of setting the cookie domain in logon. In the event handler I check whether the response is setting the authentication cookie. If it does I set the appropriate domain based on the current request. – Jarno Jun 18 '15 at 14:48

3 Answers3

0

The logic of the code is not very clear, Not clear why you are attempting to replace cookies.)

However I am going to guess that the redirection is happening before the new cookie has been registered.

        Response.Cookies.Remove(cookie.Name);

Add Code here to check if cookie is removed before you try to add the other

        Response.Cookies.Add(cookie);

Add code here to make sure the cookie has been registered by the browser (?), before you redirect

DaniDev
  • 2,471
  • 2
  • 22
  • 29
0

You can't mix host and domain cookies with the same name. To make this work all cookies will need to be set at the top level domain.

Mike
  • 3,462
  • 22
  • 25
0

Try to use following code.I hope that will help you.

if (Membership.ValidateUser(LoginUser.UserName.Trim(), LoginUser.Password.Trim())) {

                int timeout = model.RememberMe ? 525600 : 30;
                //DateTime timeout = model.RememberMe ? 525600 : 30;
                 string userData = JsonConvert.SerializeObject(model);
                 FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, login[0].adminUserName, DateTime.Now, DateTime.Now.AddMinutes(525600), false, userData);

                string enTicket = FormsAuthentication.Encrypt(authTicket);
                HttpCookie authcookie = new HttpCookie(FormsAuthentication.FormsCookieName, enTicket);
                Response.Cookies.Add(authcookie);



            return  Response.Redirect("~/Account/ChangePassword.aspx"); //authenticated area


            }
            else
            {
                Response.Write("Invalid UserID and Password");
            }
RajeshVerma
  • 1,087
  • 9
  • 13