« The Problem of Peristance: Storing and B... | Main | What a night...will it ever end? »

Here's a heck of a thing.  I'm doing my own FormsAuthentication Cookie/Ticket, rather then using the built in FormsAuthentication.RedirectFromLoginPage, as I need support for UserData, and other encrypted goodness.  So, I need to:

    // Create the authentication ticket            
            FormsAuthenticationTicket authTicket = new
                FormsAuthenticationTicket(1, //version
                userName, // user name
                DateTime.Now,             //creation
                DateTime.Now.AddMinutes(??), //Expiration
                false, //Persistent
                userDataGoodness); //Secret Sauce

but, I want to use the Timeout value that is already in the Web.config:

    <authentication mode="Forms">
            <!-- TODO: Set requireSSL to true for production -->
            <forms requireSSL="false"
                slidingExpiration="true"
                loginUrl="~/login.aspx"
                name="AuthenticationTicket"
                protection="All"
                timeout="20" />
        </authentication>

which seems reasonable, eh?  Plus, as I'm a nice guy (hopefully not a hack) I like to do things the Kosher way.  I'd had to hack this up.  So, I figure I'll use something like 'FormsAuthentication.Timeout' - except it doesn't exist.  I can get to everything else, just not the Timeout.

And the Googling and Reflecting begins.  Sometimes I think that's my full time job, Googling and Reflecting.

Here's my thought process, for your edutainment:

  • THOUGHT: Surely they must have thought about this.
  • ANSWER VIA GOOGLE: In a 2002 MSDN Online Chat, someone asked this question and was told: “Unfortunately, we don't expose this configuration property currently.“ but given these helpful tips:
    1. You could try reading it using System.Management
      (ME: But this would require giving WMI access to ASP.NET and doing something like:

      string path = "root\\NetFrameworkV1:forms";
      ManagementObject pm = new ManagementClass(path).CreateInstance();
      pm["Selector"] = "config://localhost"; // represents the machine.config file
      pm.Get();
      Response.Output.WriteLine("timeout = {0}<br>", pm["timeout"]);

      Yuck.)
    2. You can retrieve the value you set by casting User.Identity to an instance of FormsIdentity and accessing the fields on that object.
      (ME: This only allows me to see the result AFTER I've already set it once.  I need this to work the first time, and I'd like to read it as a configuration item, not a side effect.

  • THOUGHT: Someone on Google Groups must have done this before.
  • ANSWER FROM GOOGLE GROUPS: Noone has a clue, but many have hacked things worse that my upcoming hack. NOTE: Don't do this, and remember Scott's Rule of Programming 0x3eA)
    1. Private Function TimeOut_Get() As Integer
      'Get formsauthentication TimeOut value
      'Kludge, timeout property is not exposed in the class
      FormsAuthentication.SetAuthCookie("Username",
      False)
      Dim ticket As FormsAuthenticationTicket =
      FormsAuthentication.Decrypt(Response.Cookies
      (FormsAuthentication.FormsCookieName).Value)
      Dim ts As New TimeSpan(ticket.Expiration.Ticks -
      ticket.IssueDate.Ticks)
      Return ts.Minutes
      End Function

  • THOUGHT: I could just put the configuration somewhere else in my web.config and let folks keep the two in sync.
  • ANSWER FROM MY CONSCIENCE: That would mean that there would be invalid states if they didn't match.

Note, here's where insanity and over engineering set in...

  • THOUGHT: I can just use HttpContext.GetConfig and read it myself.
  • ANSWER VIA REFLECTOR: AuthenticationConfig and all it's properties are internal.

  • THOUGHT: I can use Reflection and read the privates myself. 
    Remember, Relector and a little Red Wine will always give you access to Private Members.
  • ANSWER VIA MY CONSCIENCE: I don't really want to Reflect my way to salvation

  • THOUGHT: Screw it, let's just SelectSingleNode once and feel back for 10 minutes.
  • ANSWER:

                System.Xml.XmlDocument x = new System.Xml.XmlDocument();
                x.Load(UrlPath.GetBasePhysicalDirectory() + "web.config");
                System.Xml.XmlNode node = x.SelectSingleNode("/configuration/system.web/authentication/forms");
                int Timeout = int.Parse(node.Attributes["timeout"].Value,System.Globalization.CultureInfo.InvariantCulture.NumberFormat);

I know it could be better, but I'll put it in a constructor, add some error handling and move on. All this thinking only took about 20 minutes, so don't think I spent the afternoon sweating it.  More time was spent on this post! :)

Tracked by:
"Forms Authentication Timeout" (Scott Forsyth's WebLog) [Trackback]
"Forms Authentication Cookies and Subdomain Names" (Blog-HowTo.com) [Trackback]
"Forms Authentication Cookies and Subdomain Names" (Dave's Tech Shop) [Trackback]
"slidingExpiration=&amp;quot;NotReally&amp;quot; timeout=&amp;quot;N... [Trackback]