System.Web.UI.Page oPageMy cookie is not deleting. I took a look at several articles and everything looks great, just when I step through Visual Studio (or just run under localhost) in resopnse to a button click, my cookie stays.
For whatever it is worth, I am using Visual Studio 2012 with .Net 4.0. I am debugging on localhost using the default IE (v9 on Win7/64 with all the latest updates).
public static void LoginUser(String strEmail, int iId, int iKeepDays)
{
HttpCookie oCookie = new HttpCookie("myCookie");
// Set the cookie value.
oCookie.Secure = false;
oCookie["Id"] = iId.ToString();
oCookie["Email"] = strEmail;
oCookie.Expires = DateTime.Now.AddDays(iKeepDays);
// Add the cookie.
HttpContext.Current.Response.Cookies.Add(oCookie);
}
public static void LogoutUser(System.Web.UI.Page oPage)
{
// Get the cookie.
HttpCookie oCookie = new HttpCookie("myCookie");
oCookie = HttpContext.Current.Request.Cookies["myCookie"];
if (null != oCookie)
{
// Remove the cookie.
cCookies.RemoveCookie("myCookie");
// Go back to the home page.
if (oPage.IsCallback)
ASPxWebControl.RedirectOnCallback("/");
else
HttpContext.Current.Response.Redirect("/");
}
}
/// <summary>
/// This function will be used to remove cookies value
/// </summary>
/// <param name="key"></param>
public static void RemoveCookie(String key)
{
//get cookies value
HttpCookie oCookie = null;
if (null != HttpContext.Current.Request.Cookies[key])
{
oCookie = HttpContext.Current.Request.Cookies[key];
// You cannt directly delte cookie you should set its expiry date to earlier date
oCookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(oCookie);
}
}
The answer seems obvious now that I am writing it and figured it out, however I can say that the answer was not that easy to come by, obvious or not.
The code above executes on the server, but the deletion of the cookie happens on the client. Execution has to transfer to the client and then back to the server in order for the server to recognize that the cookie got deleted.
I was reading the data back within the same logout call, just in a different function. Since accepted practice states to reset the cookie, the function wrote the cookie back. The cookie was deleted and then came back. It even got a new file name. (I opened up the hidden cookie folder.)
My solution was to pass in the login state to that other function. That resolved the cookie part.
The cCookies.RemoveCookie("myCookie"); line does not call your RemoveCookie method. The line should be RemoveCookie("myCookie"); instead.
Related
In an ASP.NET application using the OWIN pipeline, I am attempting to use cookie authentication & override CookieAuthenticationProvider.ValidateIdentity and do something similar to:
public override Task ValidateIdentity(CookieValidateIdentityContext context) {
Claim simpleClaim = context.Identity.FindFirst("SIMPLECOUNT");
if (simpleClaim != null) {
Trace.WriteLine($"SIMPLECOUNT: {simpleClaim.Value}");
if (context.Identity.TryRemoveClaim(simpleClaim)) {
var newIdentity = new ClaimsIdentity(context.Identity);
int newcount = 1 + int.Parse(simpleClaim.Value);
newIdentity.AddClaim(new Claim("SIMPLECOUNT", newcount.ToString()));
context.ReplaceIdentity(newIdentity);
}
}
return Task.FromResult<object>(null);
}
In reality I am updating other claims, and only doing this occasionally. But this highlights the main confusion. I can call context.RejectIdentity() or I can call context.ReplaceIdentity(). Presumably if I call the "reject", the old ClaimsIdentity would be a goner. So if instead I repeatedly replace a ClaimsIdentity after wiping the old claim value, and adding back the claim with an incremented value, I'd expect never to see the old value again.
But that is not the case at all. The same value keeps coming back. By playing with cookie timeout settings I can see that an incremented claim value eventually shows up once the cookie has been replaced due sliding expiration. Where is the new value even being "queued" that it would eventually update?
As for the recurring old value, is this the cookie from the browser reasserting itself? I don't want to have to sign the user out to update the claims -- in fact I am seriously only using claims as storage because I don't know what else I can use in this context (as opposed to controller methods). Is there a better approach? Ultimately what I am trying to do is synchronize cookie expirations with an updated API token written to the new cookie.
I know this is probably a bit late but I found out that if you sign the user in again the old claims are replaced with the new ones. So your case would look like this.
public override Task ValidateIdentity(CookieValidateIdentityContext context) {
Claim simpleClaim = context.Identity.FindFirst("SIMPLECOUNT");
if (simpleClaim != null) {
Trace.WriteLine($"SIMPLECOUNT: {simpleClaim.Value}");
if (context.Identity.TryRemoveClaim(simpleClaim)) {
var newIdentity = new ClaimsIdentity(context.Identity);
int newcount = 1 + int.Parse(simpleClaim.Value);
newIdentity.AddClaim(new Claim("SIMPLECOUNT", newcount.ToString()));
context.Request.Context.Authentication.SignIn(newIdentity);
}
}
return Task.FromResult<object>(null);
}
I'm currently working on getting a test environment stood up (it is currently called DEV) and am experiencing some weird issues.
When you first come to the site, we have an agreement page. Hitting the "I Agree" button will force the user through an Action to check to see if they are a member of the site already or not. We do use a demo mode also, but that is not part of the issue.
The issue I'm currently experiencing is the following. Initially in the Action, we create a Cookie called "siteaccept". Once that is created, we determine if the site is in demo mode or not, then move on to getting the user (actual user or demo user). Once the user is found, we log their Id in a Cookie called "cntPOC", and also create a Session variable by the same name with the same data (original developers wrote much of this convoluted logic which I want to change before someone asks why keep a Session and Cookie). We then do a RedirectToAction to the Action to bring up the main page of the site.
Here is where the issue comes into play. The main page of the site's Action has a CustomAuthorizeAttribute decoration on it. In our CustomAuthorizeAttribute class, we have OnAuthorizion and AuthorizeCore being overrode. OnAuthorizion fires off first, however, it uses base.OnAuthorization. Once that is called, AuthorizeCore is called. In AuthorizeCore, we check for the "siteaccept" Cookie, followed by a check on the "cntPOC" Session variable. If both are there, we return true, otherwise false if either fails.
On not only my local environment but the DBA's, this works without a hitch. I see our Cookies and Session variable. However, on our DEV environment, both the Cookies and Session variable are missing. We have IE 11 configured to allow Cookies, yet we cannot get them once we leave the Action and proceed into the CustomAuthorizeAttribute.
I did find I can find the Cookie today if I check HttpContext.Current.Response instead of HttpContext.Current.Request, but that is the incorrect way to do it obviously.
Below is my code. I'm fairly certain since the code works on my local environment, it should be fine in our DEV environment. Also a quick note, our production environment does work, so the code obviously functions. It's a question now of why does the DEV environment not.
MainController.cs
[HttpPost]
public ActionResult Index(FormCollection frmCollection)
{
try
{
Response.Cookies.Remove("bracmisaccept");
HttpCookie cookie = new HttpCookie("bracmisaccept");
cookie.Value = "true";
Response.Cookies.Add(cookie);
...
//Demo Mode
var poc = new HttpCookie("cntPOC");
cookie.Value = "7578";
Response.Cookies.Add(poc);
Session["cntPOC"] = 7578;
return RedirectToAction("ApplicationSelection");
}
catch (Exception ex)
{
logger.LogError("Main Index", ex);
return PartialView(#"../Error/ExceptionHandling");
}
}
[CustomAuthorizeAttribute]
public ActionResult ApplicationSelection()
{
return View();
}
CustomAuthorizeAttribute.cs
public string RedirectUrl = "~/Main/SessionTimeout";
public string CookieExpiredRedirectUrl = "~/Main/Index";
public string AjaxRedirectUrl = "~/Error/AjaxError";
private bool _isAuthorized;
private bool _isCookieExpired;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (HttpContext.Current.Request.Cookies["siteaccept"] == null)
{
_isAuthorized = false;
_isCookieExpired = true;
return false;
}
if (HttpContext.Current.Session["cntPOC"] == null)
{
_isAuthorized = false;
return false;
}
return true;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!_isAuthorized)
{
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.HttpContext.Response.End();
}
else
{
if(_isCookieExpired)
filterContext.RequestContext.HttpContext.Response.Redirect(CookieExpiredRedirectUrl);
else
filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl);
}
}
}
I'm fairly certain the code is fine, but I did read in a few articles that AuthorizeCore may or may not have the Cookies and Session variables at times. I just wanted to find out if I'm wasting my time with changing the code or if it's the box we have this site on. The server is super locked down, so yeah, kind of annoying...
Edit: I have yet to figure out how to fix this yet, however, I did find if I do a publish on this code, I can enter into the site properly. I still cannot run localhost to inspect the site, but a publish fixes a few minor issues of whether things will work on this site.
I want my page to set cookies on users in order to track when and if they return, and will be keeping their unique visitor id in a database. I want to avoid creating cookie records for users which do not accept cookies (such as bots and crawlers), so I need a way to check if they are accepting cookies or not. I've devised the following code.
private bool CookiesAreEnabled()
{
bool result = false;
HttpCookie CookieChecker = new HttpCookie("CookieChecker");
CookieChecker.Value = "Do you see me?";
CookieChecker.Expires = DateTime.Now.AddSeconds(10.0d);
Response.Cookies.Set(CookieChecker);
CookieChecker = new HttpCookie("CookieChecker");
CookieChecker = Request.Cookies["CookieChecker"];
if (CookieChecker != null)
{
result = true;
CookieChecker = new HttpCookie("CookieChecker");
CookieChecker.Value = "";
CookieChecker.Expires = DateTime.Now;
Response.Cookies.Set(CookieChecker);
}
return result;
}
It seems to me that this should detect that cookies are disabled, but it doesn't! In my testing so far using Firefox with cookies turned off, the code reports that cookies are enabled! Am I barking up the wrong tree as far as detecting if cookies are enabled? Or am I making a newby-style mistake?
Not sure if overall this code is right, but Response.Cookies.Set(CookieChecker); will set on response that browser is yet to receive and process. when you immediately after call Request.Cookies["CookieChecker"]; this examines current request you're processing - one that browser generated before even receiving Set-Cookie request.
At a minimum you need to let browser process response, then examine the next request to see if the cookie is there.
if you want to use that approch the only way is to use 2 request: the first you set a cookie, the second you read it. You cannot do it with only one request.
Otherwise I think you can achieve it with javascript (this is a pseudo code):
/*first you check if there is already a cookie with the identifier*/
if(document.cookie.indexOf('cookiewithuniqueidentifier') == -1) {
document.cookie = 'testcookie';
cookieEnabled = (document.cookie.indexOf('testcookie') != -1) ? true : false;
if (cookieEnabled) {
/*ajax request to the server for requesting an unique identifier*/
/*save a cookie with that identifier*/
document.cookie='cookiewithuniqueidentifier=936DA01F-9ABD-4d9d-80C7-02AF85C822A8'
}
}
So, what I am trying to accomplish is a basic "remember me" style action for users of my application.
I have completed writing everything so far, and it is working as expected most of the time. Occasionally though, the method to check for the persistent Forms Authentication ticket doesn't auto login, and I can't figure out why it is only happening occasionally.
To test my code, what I have done is start the debugger, manually kill my session cookie in chrome's dev tools, then reload the page. Stepping through the code, it enters into the auto login method as expected and proceeds to reset my session data. However, if I wait an inordinate amount of time, like 4 hours perhaps, and try the same thing it does not auto reset my session. (Assuming that i've left the debugger running for that amount of time).
EDIT: For clarity's sake, when this error is happening, I can open the dev tools and see that the authentication ticket is still available. It's just the code to reset my session is either not running, for erroring out somewhere. Due to the infrequency in which this is happening, it's hard to track down.
So, onto the code.
I'm calling the static void auto login method in the controller's constructor, and passing the httpcontext into the auto login method.
Controller
public class SiteController : Controller
{
public SiteController()
{
this.UserAutoLogin(System.Web.HttpContext.Current);
}
// GET: /Site/
public ActionResult Index()
{
ViewBag.CatNav = this.RenderNavCategories();
return View();
}
}
Auto Login Code
public static void UserAutoLogin(this Controller Controller, System.Web.HttpContext context)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(FormsAuthentication.FormsCookieName);
if (cookie != null)
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
if (ticket != null)
{
if (ticket.Name.Length > 0)
{
try
{
if (context.Session["UserName"] == null)
{
//get user from db
PersonRepository PersonRepo = new PersonRepository();
PersonModel Member = PersonRepo.GetUserUserName(ticket.Name);
if (Member.FirstName != null) //if this is null...then the cookie is wrong, so don't do shit
{
//Set the session parameters
context.Session["FirstName"] = Member.FirstName;
context.Session["LastName"] = Member.LastName;
context.Session["UserId"] = Member.Id;
context.Session["UserName"] = Member.Username;
context.Session["Email"] = Member.Email;
context.Session["IsUser"] = 1;
context.Session["Zip"] = Member.Zip;
FormsAuthentication.SignOut();
FormsAuthentication.SetAuthCookie(Member.Username, true);
}
}
}
catch (Exception ex)
{
// don't do anything for now - do something smart later :)
Console.WriteLine(ex.ToString());
}
}
}
}
}
Because when IIS is recycling the app, a new machine key is generated. The FormsAuthentication ticket is signed using that key so when the key changes the old ticket isn't recognized. You need to use a fixed machine key.
Edit: Removed link to key generator site (now defunct)
In the HttpCookieCollection.Get MSDN documentation, it is stated that:
If the named cookie does not exist, this method creates a new cookie
with that name.
This is true and works well when calling HttpContext.Request.Cookies or HttpContext.Response.Cookies from a "real" web server.
However, this code:
HttpCookieCollection foo = new HttpCookieCollection();
HttpCookie cookie = foo.Get("foo");
Console.WriteLine(cookie != null);
Displays False (cookie is null).
This is not the case if the HttpCookieCollection is retrieved from Request.Cookies in a HTTP handler.
Any idea of what is wrong here/if any other setup is needed?
I'm asking this because I write unit tests where I mock HttpContextBase, so no "real" context is provided.
Thank you for your help
If you look at the code for HttpCookieCollection.Get(string), you'll see something like this:
public HttpCookie Get(string name)
{
HttpCookie cookie = (HttpCookie) this.BaseGet(name);
if (cookie == null && this._response != null)
{
cookie = new HttpCookie(name);
this.AddCookie(cookie, true);
this._response.OnCookieAdd(cookie);
}
if (cookie != null)
this.EnsureKeyValidated(name, cookie.Value);
return cookie;
}
It's never creating the cookie because _response is going to be null (look at the first 'if' statement). i.e. there's no response object to send the new cookie back to, so it won't create it.
The response object is an HttpResponse object and it's passed through to the internal constructor (so that constructor is not available to you).
I personally never liked the way the Get method acts on HttpCookieCollection; it violates the Command-Query separation principle: asking a question should not change the answer.
I would recommend you check for the existence of the cookie by checking the AllKeys property; if it doesn't exist, explicitly create and add the cookie to the collection. Otherwise, if you know the key exists, go ahead and fetch the existing entry. Then your production code and unit tests should behave.
It might be a good idea to create a helper or extension method to use instead of Get, to ensure it behaves as you expect whether you're unit testing or running normally:
public static class HttpCookieCollectionExtensions
{
public static HttpCookie GetOrCreateCookie(this HttpCookieCollection collection, string name)
{
// Check if the key exists in the cookie collection. We check manually so that the Get
// method doesn't implicitly add the cookie for us if it's not found.
var keyExists = collection.AllKeys.Any(key => string.Equals(name, key, StringComparison.OrdinalIgnoreCase));
if (keyExists) return collection.Get(name);
// The cookie doesn't exist, so add it to the collection.
var cookie = new HttpCookie(name);
collection.Add(cookie);
return cookie;
}
}