So I have a work around for this, however if anyone has any better ideas please feel free to comment. Essentially you need to intercept the response at the end of the request and manually set the Secure property on the forms authentication cookie, pretty obvious really, you will also need to set the requireSSL property in the forms authentication configuration to false. Also bear in mind we do not want to enable HTTPS for the entire site for authenticated users hence this work around.
There are a couple of caveats to this approach and a few things to be aware of.
I found during testing that the forms authentication cookie was always written to in the response, so I kept overwriting the valid authentication cookie in the browser with an empty authentication cookie, to get around this I included some logic in the HTTP module to work around this, see code snippet below.
All requests to the application which require authorization must be under SSL, otherwise the request will not contain the authentication cookie in order to authenticate the user.
Because you are only passing the authentication cookie for SSL requests you will need another mechanism to tell your application that the current user is authenticated when they browse the non SSL areas of the site, I have implemented this with an additional cookie which is set when the user logs in, and does not have an expiry date set, so will expire at the end of the users session, of course this cookie is removed if the user logs out.
Below is the logic implemented in an HTTP Module to affect the above, I have been testing this the last couple of hours and have not come across any problems yet, I will be sure to update this post if I do!
private void EndRequest(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
if (ValidRequest(application.Request) && application.Response.Cookies.Count > 0)
{
//we should only ever send an authentication cookie to the client if the user has just logged in here's the logic
/*
* 1. If the request has an auth cookie the user is already authenticated and under SSL
* so ensure we do not send a new auth cookie in the response.
* 2. If the request does not have an auth cookie but there is a valid auth cookie in the
* response, set the response auth cookie to secure, so it is only transmitted by the
* browser under SSL.
* 3. If the request does not have an auth cookie and the response has an invalid or empty
* auth cookie, ensure we remove the response cookie so we dont overwrite the valid cookie
* in the client browser.
*/
//only do the below if the user is not logging out the site, if the user is logging out we can
//leave the default forms authentication behaviour which is to expire the auth cookie
if (application.Request.AppRelativeCurrentExecutionFilePath != "~/authentication/logoff")
{
var requestAuthCookie = application.Request.Cookies[FormsAuthentication.FormsCookieName];
var responseAuthCookie = application.Response.Cookies[FormsAuthentication.FormsCookieName];
if (requestAuthCookie != null && responseAuthCookie != null && responseAuthCookie.Value.IsNullOrEmpty())
{
application.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
}
else if (responseAuthCookie != null && !responseAuthCookie.Value.IsNullOrEmpty())
{
responseAuthCookie.Secure = true;
application.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
application.Response.Cookies.Add(responseAuthCookie);
}
else if (responseAuthCookie == null || responseAuthCookie.Value.IsNullOrEmpty())
{
application.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
}
}
}
}