Cookies getting updated instead of adding - c#

I am working on a voting mechanism application in my MVC project, I have a requirement that the users are allowed to vote only once in a browser, if he reaches the voting page for the second time in the same browser he shouldnot be able to vote but if in another browser he logins and he should be able to vote again. To do that I have used the Cookies to handle this scenario. What I did was I have added the UserID to the cookie and in the View page i am checking whether Vote button is visible or not. My view is
#if ((ViewData["CookieData"] == null) || (ViewData["CookieData"].ToString() != Session["LoginPersonID"].ToString()))
{
<button name="button" value="Insert" onclick="return confirm('Are you sure you want to submit your choices ?.');">Vote Now!!</button>
<br />
#Html.ActionLink("Back to Home Page", "EmployeeHomePage", "Employee", new { logedinperson = Session["LoginPersonID"] }, null);
}
and My Controller is
public void AddCookie()
{
HttpCookie MyCookie = new HttpCookie("MyCookie", Session["LoginPersonID"].ToString());
MyCookie.Expires = DateTime.Now.AddDays(30);
Response.Cookies.Add(MyCookie);
ViewData["CookieData"] = MyCookieCollection;
}
when the First User say PersonA logins in Firefox and votes, his personid is stored in the cookie, so if he tries to reach the voting page again the Vote button will be invisible, but then he tries open on Google Chrome he can then vote.Thats also working as expected. Now when the PersonB login in Firefox and votes his personid is updated in the cookie, so he also cannot vote in the same browser for the second time. But my exact problem is when the PersonA logins again to Firefox he is able to vote because the cookie has been updated with the PersonB's id. So is there anyway how can I restrict both PersonA and PersonB if they have voted already in a browser.

One problem that I see is that you never read the cookie from the request. Your controller is always creating the cookie and sending it back with the response. Instead you should first check to see whether a cookie exists and then act accordingly.
The other aspect is that if you want the cookie to be per account you need to create a different cookie for each account (i.e. construct the cookie name based on the userid or username), or to store all ids/usernames in the cookie and then check each against each one.

The problem is that you don't distinguish between users when naming your cookie. There should be one cookie per user OR you should build a list of users in that single cookie -- but keep in mind that a cookie is limited to 4K in size.
So if you change the line
HttpCookie MyCookie = new HttpCookie("MyCookie",
Session["LoginPersonID"].ToString());
to something like this
var userName = Session["LoginPersonID"].ToString();
HttpCookie MyCookie = new HttpCookie("MyCookie-" + userName,
"Voted");
then it should work (bearing in mind that you also have to modify the code that reads the cookie).

The idea
Store both the IDs in one cookie. When the cookie is an ID list (e.g. comma-separated), it could hold voting restrictions on more than one person. You have to check that the person trying to vote is not in the list.
Of course this works only under assumption that no ID contains chosen delimiter, in this case comma. If comma is not the case, choose pipe or whatever character satisfies this property.
Algoritm
Cookie mechanism is designed to store key-value pairs. Both key and value are strings. If you want to store a comma-separated list of IDs in your cookie, you have to join them using comma as a delimiter (String.join(",", ids)). First read the original value of your cookie, split it in commas, then add the new ID (if not already there) and then join the list and update the cookie.
Implementation
public void AddCookie() {
HttpCookie MyCookie = new HttpCookie("MyCookie");
var userName = Session["LoginPersonID"].ToString();
var originalCookieContents = ViewData["CookieData"].ToString();
var ids = new HashSet<String>(originalCookieContents.Split(','));
ids.Add(userName);
MyCookie.Value = String.Join(",", ids);
MyCookie.Expires = DateTime.Now.AddDays(30);
Response.Cookies.Add(MyCookie);
ViewData["CookieData"] = MyCookie.Value;
}
Do not forget to update your view code accordingly! Construct HashSet ids and use its Contains method.

Related

How can I delete this cookie? ASP.NET MVC

I try to delete this cookie:
First of all a bit of background. This is a token for verification on different sites within the same domain. I make the central login page. This page is working fine, except the log out. Every other cookie, I want to delete, gets deleted by JQuery cookie $.removeCookie('cookieName'). And I know, I can't delete this from the JavaScript, because of the secure-flag.
So I tried deleting it from a controller method. I call this method via ajax from JavaScript.
$.ajax({ur:'/Login/ExpireToken'})
I can see that it works in the Chrome Console Network Tab.
public void ExpireToken()
{
Response.Cookies.Remove("crowd.token_key");
}
But this, I don't know why, makes a new cookie, with the same name, empty value, the whole domain of the login page and no flags set.
So I tested, if the backend can find the cookie I want.
public string ExpireToken()
{
return Response.Cookies["crowd.token_key"].Value;
}
It returns the correct value and doesn't create a new/false one.
After this I tried to set the expires field to one day in the past or to now. I don't know, why this should work, because the expiration date of this cookie is already in the past.
public void ExpireToken()
{
Response.Cookies["crowd.token_key"].Expires = DateTime.Now.AddDays(-1d);
}
And guess what, it doesn't work. It does literally nothing.
Other ways that don't work
if (Request.Cookies["crowd.token_key"] != null)
{
var c = new HttpCookie("crowd.token_key");
c.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(c);
}
As per the doc, you are doing things right in your las attemp, the one setting the expiration date to yesterday. Quote:
The technique is to create a new cookie with the same name as the
cookie to be deleted, but to set the cookie's expiration to a date
earlier than today. When the browser checks the cookie's expiration,
the browser will discard the now-outdated cookie
I would put a breakpoint and debug to check cookie names, if everything is fine, perhaps the web browser is missbehaving.
HttpCookie aCookie;
string cookieName;
int limit = Request.Cookies.Count;
for (int i=0; i<limit; i++)
{
cookieName = Request.Cookies[i].Name;
aCookie = new HttpCookie(cookieName);
aCookie.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(aCookie);
}

how to create a cookie which can store list/Array

In my current project I have to show a Popup(Welcome Info) every time he/she login to the site. In the Popup user have a option(Onclick CheckBox) to hide the Popup for 30days.
I'm achieving this by saving a UserUniqueId to cookie(Which should be an array/list) when he/she click checkBox on popup. As Below
var UniqueId = "xxx";
var uIDCookies = [];
// looping to get all the values that are stored in cookie
$.each($.cookie(), function(i, v) {
uIDCookies.push(i);
});
// if current userUID not found in cookie then showing popup
if (($.inArray(UniqueId, uIDCookies)) === -1) {
//show popup
} else {
// hide popup
}
// create cookie and set expire time
$('.checkBox').on('click', function () {
var date = new Date();
var minutes = 3;
date.setTime(date.getTime() + (minutes * 60 * 1000));
$.cookie(UniqueId, { expires: date, path: '/' });
//hide popup
});
No matter what I'm looping through all cookie values that are stored on client browser so, I feel that there could be a better way to achieve this. Suggestions Please..!!
You are saving the cookie with the UniqueID as the name. Just check if that cookie already exists directly:
if (! $.cookie(UniqueId) ) {
//show popup
} else {
// hide popup
}
This sounds very suspiciously like a very bad design choice, but to answer the question as asked, just serialise the array and convert the string to Base64. Then you can parse it back out again afterwards.
As mentioned in the OP's comments though, there are very few edge cases where this is necessary in the scenario you have mentioned. Also, don't set or get cookies in jQuery. Use your serverside language to gather and set cookie information whereever possible, and send it to the page where needed. Don't use cookies where it would be better to use a data store. You have no control over the cookie information once it has been set, and so they can be a security vector into your application.
Cookies are stored per profile, per browser, per user, per computer, and so aren't always the best way to store this info. Instead, consider adding two columns to your user profile table, DisplayWelcomeMessage as a boolean, and SuppressWelcomeMessageExpiry as a DateTime. That way, the info is set per user, not per cookie. It allows the application to be more scalable, as it gives the user the option to turn it back on before the expiry runs out.

How do I manually delete a cookie in asp.net MVC 4

I need to delete authentication cookie manually (Instead of using FormsAuthentication.SignOut whcih for some reasons does not work). I tried
System.Web.HttpContext.Request.Cookies.Remove(cookieName); // for example .ASPXAUTH
System.Web.HttpContext.Response.Cookies.Remove(cookieName); // for example .ASPXAUTH
FormsAuthentication.SignOut(); // I don't know why this one does not work
Neither of those command work. In fact Response cookies are empty and request cookie contains the cookie I want to delete when the following commands are executed it no longer contains the cookie I deleted but in browser the cookie still exists and I am able to do things that authorized users can even after signing out.
Try:
if (Request.Cookies["MyCookie"] != null)
{
var c = new HttpCookie("MyCookie")
{
Expires = DateTime.Now.AddDays(-1)
};
Response.Cookies.Add(c);
}
More information on MSDN.
c.Expires = DateTime.Now.AddDays(-1);
This does not clear cookies instantly.
Use this: c.Expires = DateTime.Now.AddSeconds(1);
This will clear cookies instantly.

MVC Handling a CorpId for the site

I'm not sure I'm handling this the right way, but since I'm running into issues, I assume I'm not.
I have to have a corporation id sent in when loading the login screen.
Looks like this:
public ActionResult LogOn(string id)
{
var sb = new StringBuilder();
sb.AppendLine(string.Format("CorpID: {0}", id));
if(ViewBag.CorpID != null)
sb.AppendLine(string.Format("ViewBag.CorpID: {0}", ViewBag.CorpID));
Guid corpIdGuid;
if (!Guid.TryParse(id, out corpIdGuid) && string.IsNullOrEmpty(ViewBag.CorpID))
return null;
// the id passed in will take presidence over the
// viewbag unless it is blank then we use viewbag
// one way or the other viewbag.corpid should not
// be blank
if(!string.IsNullOrEmpty(id))
ViewBag.CorpID = id;
// Session["CorpId"] = id;
//Not a junk guid.. continue.
return View();
}
I need this to establish what company we will be working with during this session.
The problem I am running into, is when the cookie timeout occurs, which is set to 10 minutes, it directs them back to this login and I have no corpid anymore.
I tried the viewbag and it's being reset.
I tried a cookie, but since it expires, the data is no longer there.
I tried a Profile Manager but since they are logged it, that puts me back to nothing.
How do I maintain this CorpId when the user has timed out and put back on the login screen? I need this information for each screen I have also.
Any input would be greatly appreciated!
You need to create a separate cookie that identifies the Corporate ID that doesn't expire with user's session. Session["CorpId"] will expire with the user session and won't work.
var corpCookie = new HttpCookie("CorpID", id);
corpCookie.Expires = DateTime.Now.AddDays(30.0);
HttpContext.Current.Response.Cookies.Set(corpCookie);
Each time the user logs in, you could update the expiry to make it a sliding expiration. To retrieve the cookie value, use the following:
var corpID = HttpContext.Current.Request.Cookies.Get("CorpID").Value;

FormsAuthentication after login

Ok, i have simple scenario:
have two pages:
login and welcome pages.
im using FormsAuthentication with my own table that has four columns: ID, UserName, Password, FullName
When pressed login im setting my username like:
FormsAuthentication.SetAuthCookie(userName, rememberMe ?? false);
on the welcome page i cant use:
Page.User.Identity.Name
to provide to user which user currently logged, BUT i dont user username like at all examples in http://asp.net web site i want to user FullName field
i think that always go to db and request fullname when page loads its crazy and dont like to user Sessions or Simple Cookie mayby FormsAuth provider has custom fields for this
I would store the user's full name in the session cookie after your call to FormsAuth
FormsAuth.SetAuthCookie(userName, rememberme);
// get the full name (ex "John Doe") from the datbase here during login
string fullName = "John Doe";
Response.Cookies["FullName"].Value = fullName;
Response.Cookies["FullName"].expires = DateTime.Now.AddDays(30);
and then retrieve it in your view pages via:
string fullName = HttpContext.Current.Request.Cookies["FullName"].Value
Forms authentication works using cookies. You could construct your own auth cookie and put the full name in it, but I think I would go with putting it into the session. If you use a cookie of any sort, you'll need to extract the name from it each time. Tying it to the session seems more natural and makes it easy for you to access. I agree that it seems a waste to go back to the DB every time and I would certainly cache the value somewhere.
Info on constructing your own forms authentication cookie can be found here.
Sorry I'm a little late to the party, but here's how you can do this without storing the value anywhere else :)
var authCookieKey = FormsAuthentication.FormsCookieName;
var responseCookies = HttpContext.Current.Response.Cookies;
var requestCookies = HttpContext.Current.Request.Cookies;
var aspxAuthCookieInResponse = responseCookies.AllKeys.Contains(authCookieKey) ? responseCookies[authCookieKey] : null;
var aspxAuthCookieInRequest = requestCookies.AllKeys.Contains(authCookieKey) ? requestCookies[authCookieKey] : null;
// Take ASPXAUTH cookie from either response or request.
var cookie = aspxAuthCookieInResponse ?? aspxAuthCookieInRequest;
var authTicket = FormsAuthentication.Decrypt(cookie.Value); // Todo: Check for nulls.
// Using the name!
var userName = authTicket.Name;
There are no custom fields for forms authentication. You'll just have to use session. That's what it's there for you know. ;) Just don't forget - forms authentication cookie and session are two independant things. They even each have their own timeouts. So the session won't be reset when a user logs out unless you do so yourself.
What about using Profiles to store the extra info with the User?
The simplest option is to use the session. By default session state is stored in memory and will be lost when the ASP.NET worker process recycles, however you can configure it to use state service instead, which retains session info in a separate process:
http://msdn.microsoft.com/en-us/library/ms178586.aspx
Another option would be to use profiles. As it sounds like you already have 'profile' information stored in your own tables, you'd probably have to write a custom provider for it so it's a more complex solution.

Categories