13

I have an MVC 4 website where a user can login and I save a cookie with their session information so they don't have to login again.

public void SetCookie(HttpCookie cookie)
{
    HttpContext.Current.Response.Cookies.Set(cookie);
}

This works on desktop devices, but when I run this on iOS, it doesn't work 100% of the time. If I leave at least 1 page open in the mobile browser (either Chrome or Safari), and navigate back to my site, my session cookie is found and I don't have to login. But if I close ALL windows of that browser, then the next time I navigate back to my site, the session cookie is not found. I'm setting a 1 year duration/expiration on my cookie so it's not expiring.

The only thing I've found so far is that this is a bug in .NET 4.0 and is fixed in .NET 4.5. I believe I'm already running .NET 4.5, by looking at my *.csproj and the TargetFrameworkVersion element:

<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{xxxxxxxxxxxxxxxxxx}</ProjectGuid>
    <ProjectTypeGuids>{xxxxxxxx};{xxxxxxxxxxxxx};{xxxxxxxxxxxxxxxx}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>MyApp</RootNamespace>
    <AssemblyName>MyApp</AssemblyName>
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <MvcBuildViews>false</MvcBuildViews>
    <UseIISExpress>true</UseIISExpress>
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication />
    <IISExpressWindowsAuthentication />
    <IISExpressUseClassicPipelineMode />
  </PropertyGroup>

Is this a bug with .NET (or MVC), or is it my coding? Do I need to upgrade to MVC 5 (instead of the original recommendation of upgrading to .NET 4.5)?

This has really been bugging me since the majority of the traffic to my site in the next coming months will be from mobile users, and I'll probably lose some of them if they have to keep logging in everytime (I know I hate having to login on a mobile device...)

Edit:

Btw - Here's another page on the same subject: Asp.Net Forms Authentication when using iPhone UIWebView

And I've also tried implementing this and it didn't work either:

http://www.bloggersworld.com/index.php/asp-net-forms-authentication-iphone-cookies/

And the above includes Scott Hanselmans suggested fix:

http://www.hanselman.com/blog/FormsAuthenticationOnASPNETSitesWithTheGoogleChromeBrowserOnIOS.aspx

Here's my cookie creation code, in case it helps:

private void CreateAndSetAuthenticationCookie(int loginId, string username)
    {
        HttpCookie cookie = CreateAuthenticationCookie(loginId, username);

        _cookieService.SetCookie(cookie);
    }

    private HttpCookie CreateAuthenticationCookie(int loginId, string username)
    {
        string userData = string.Format("loginId:{0},username:{1}", loginId, username);
        var ticket = new FormsAuthenticationTicket(loginId, username, DateTime.Now, DateTime.Now.AddYears(1), false, userData, FormsAuthentication.FormsCookiePath);
        string encryptedTicket = FormsAuthentication.Encrypt(ticket);

        return new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
    }

One thing that I pointed out in my comments, I have "PersistentCookie" set to false....not sure if that makes a difference, I'll do more research.

Update:

First of all, the PersistentCookie is set to true now, and that did not have any changes on the behavior.

One of the commentors below suggested that I run Fiddler while accessing the website from iPhone. After going through the very short and easy steps to get that setup, here's what I found out.

The first request I tried pointed out (I believe) what the actual problem is. When I examined that first request, iOS Safari is sending a DNT header. Having no idea what that was, I looked it up and it's a "Do Not Track" header.

Next, I went and checked my Safari settings to turn that off. Guess what, it's already off. Why the hell is Safari (iOS) sending a DNT header when the setting (Settings -> Safari -> Do Not Track) is not set? Also, the Block Cookies, which is right below it, is set to "Never".

After getting frustrated with that, I went to check on Chrome for iOS to see if that still doesn't work. IT WORKS! From what I can tell, I'd close all tabs, try again, close all tabs, then kill the browser, and it still works. Hooray!

Now, I need to figure out why iOS Safari is sending the DNT header...

Community
  • 1
  • 1
ganders
  • 7,285
  • 17
  • 66
  • 114
  • Wow, after two weeks, I've finally found this: http://www.bloggersworld.com/index.php/asp-net-forms-authentication-iphone-cookies/ but I'm too tired to try this tonight...will get to try it soon. – ganders Aug 02 '14 at 05:48
  • Ugh, that was a no-go...Bueller???? – ganders Aug 05 '14 at 03:15
  • just a guess, but do you use an iframe? apple devices don't like 3rd party cookies which could be a problem. – fuchs777 Aug 07 '14 at 08:50
  • @fuchs777 nope, no iframe's or any embedded type stuff – ganders Aug 07 '14 at 11:26
  • @fuchs777 Found this post about cookies and got me wondering, I have the "PersistentCookie" set to false, does that make a difference? http://stackoverflow.com/questions/8852057/why-is-my-asp-net-mvc-persistent-auth-cookie-not-working – ganders Aug 07 '14 at 18:12
  • Nope, didn't matter... – ganders Aug 07 '14 at 18:52
  • 1
    let's start with some basic debugging... is the cookie actually disappearing or is it being sent to the server and the server ignores for some reason? look at the HTTP traffic to see exactly what is going back and forth over the wire. use fiddler for that. http://docs.telerik.com/fiddler/configure-fiddler/tasks/configureforios – Robert Levy Aug 08 '14 at 00:57
  • Side note: be careful with usage of "session cookies" phrase - that may mean "session" as opposite of "persistent" *or* ASP.Net "session" cookie. May need clarification in the post. – Alexei Levenkov Aug 08 '14 at 16:21
  • @RobertLevy How do I debug this when it only occurs on iPhone (for me), and my application is hosted on App Harbor (both test and Prod)? (Since I couldn't run Fiddler there) Is it possible to run a Development version on my machine at home, and have the iPhone point to that? – ganders Aug 11 '14 at 15:40
  • 1
    if you follow those instructions, fiddler will let you see all HTTP/HTTPS traffic between your phone and the internet – Robert Levy Aug 11 '14 at 16:26
  • @RobertLevy my bad, I didn't check your link... – ganders Aug 11 '14 at 17:45
  • @RobertLevy great. Now I see that the cookie is DNT, which is Do Not Track. I checked the settings for iOS on the device and Do Not Track is NOT checked. Is that coming from my site? – ganders Aug 12 '14 at 05:26
  • another cause might be the IIS as it estimates browser capabilities http://stackoverflow.com/questions/4158550/asp-net-forms-authentication-when-using-iphone-uiwebview – fuchs777 Aug 25 '14 at 11:30
  • @fuchs777 I believe I've already tried that (was so long ago), but let me make sure tonight... – ganders Oct 14 '14 at 18:49
  • @fuchs777 I checked out your link regarding the "another cause might be the IIS..." comment and I've actually already implemented the browser files in my project, and that had no effect on this. I guess I'm still searching 4 months later... – ganders Dec 01 '14 at 18:32
  • @ganders sorry to hear it didn't help... for now I am out of ideas – fuchs777 Dec 03 '14 at 15:28

2 Answers2

33

This is HORRIBLY embarrasing. I found out that I've had my iOS Safari browser in "Private Browsing Mode" FOR YEARS!

I feel like my "Software Developer" job title needs removed for awhile. Sorry for wasting everyone's time.

ganders
  • 7,285
  • 17
  • 66
  • 114
3

I think you answered your own question with persistent cookie. Regular cookies expire when the browser session ends (this is typically closing the browser). Persistent cookies have a date when they should be removed and can live across browser sessions.

Your code should look something like this:

private HttpCookie CreateAuthenticationCookie(int loginId, string username)
{
    string userData = string.Format("loginId:{0},username:{1}", loginId, username);
    var ticket = new FormsAuthenticationTicket(loginId, username, 
                         DateTime.Now, DateTime.Now.AddYears(1), 
                         false, userData, FormsAuthentication.FormsCookiePath);
    string encryptedTicket = FormsAuthentication.Encrypt(ticket);

    return new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket){{
         Expires = DateTime.Now.AddYears(1);
    }};
}

This way your cookie will persist across browser sessions for one year.

Jon
  • 333
  • 1
  • 6
  • I tried this already, (my comment "Nope, didn't matter"). I can try putting everything in the same method to see if that makes a difference though...(my version has the ticket in one method and the cookie in another) – ganders Aug 11 '14 at 15:42
  • Using something like firebug/chrome dev tools, can you show what your cookie looks like to the client? – Jon Aug 11 '14 at 18:07
  • I'm upvoting this answer, but not accepting because this only fixed iOS Chrome, NOT Safari. – ganders Aug 14 '14 at 12:47