Options for preserving state - c#

I have a page where I need to check for the presence of a cookie and then perform a redirect.
I have the code written (ASP.NET) to detect the cookie and perform a redirect. Pseudo-code:
HttpCookie myCookie = Request.Cookies.Get("theCookie");
if(myCookie == null)
{
myCookie = new HttpCookie("theCookie","myValue")
response.Redirect("page.aspx"); //Redirect to check for the presence of the cookie
}
More code...
When the user has cookies enabled, this approach works fine. When they have cookies disabled, however, they wind up stuck in an infinite loop (the page attempts to create the cookie, redirects, sees no cookie, then redirects again, ad infinitum). Most human users are probably going to be OK, but this will probably do a number on the site's SEO ratings.
I've wracked my brain for solutions, and since cookies are out of the question, that leaves viewstate and querystrings.
Because I've got to do a redirect, I think I'm stuck with querystrings. The problem is in order to detect whether a page has already been hit, I need to append a querystring to prevent the redirect from kicking in again.
Can anyone think of a way to accomplish this (preventing a redirect) without using cookies, viewstate, or querystrings? I think the answer is probably no...

Using a querystring in the manner you have described is the correct solution.

EDIT: looks like bad idea - query string is the approach. Keeping for reference: local storage as well as script can be disabled, so it needs to be considered when designing any client side detection logic.
You can also try to use local storage in browser to prevent infinite redirects. It will allow you to keep url clean from "?isCookie=true" query string. Note that you need JavaScript enabled for that. If you worried about cookies you should be worried about JavaScript disabled too.

What I wound up doing (in case someone else tries this) was using a couple of tricks:
Set up an ASP.NET hidden field control with its value set to false (default - cookies are disabled)
Used client-side script to check whether cookies are enabled
If the cookie gets set, cookies are enabled. In this case I use a jQuery call to change the value of the hidden field from false to true
Server-side, if the value of the hidden field is true, the page does the redirect. Otherwise, it just continues processing the page.
If the user has Javascript disabled, the value of the hidden field remains false, so the page is still rendered only once.
Thanks for letting me talk through it and getting me thinking!

Related

Session variable value changes during page redirect. - Response.Redirect

I have developed a login page for a tracker. I have verified the username and password by validating the same against the DB.
When the validation is successful, I have just set the session variable with username as it is required throughout the session. But the session variable changes when it is redirected to the next page.
When retrieving the username in the next page, the session variable has been changed. The code is pasted below. Please help.
Code in Login page:
if (isValidUser)
{
Session["LoginUserName"] = Name;
Response.Redirect("xxx.aspx",false);
}
Code in xxx.aspx:
User= Session["LoginUserName"].ToString();
//Getting different value while retrieving User from session.
If you specify endResponse: false, then processing of the page & any events continues despite the Redirect which was added to the output stream. This could well change the Session.
Try calling Redirect without the endResponse: false parameter:
Response.Redirect("xxx.aspx");
If you specify endResponse: true or if you leave it out entirely, then processing of the page is silently aborted after the Redirect with a harmless ThreadAbortException. AFAIK this is the normal way of doing a Redirect, and you need to have good reasons to NOT abort after a Redirect, also you need to take great care through all your remaining code because page processing continues.

ASP.NET C# Sessions SessionID changes on Page_Load() [duplicate]

Why does the property SessionID on the Session-object in an ASP.NET-page change between requests?
I have a page like this:
...
<div>
SessionID: <%= SessionID %>
</div>
...
And the output keeps changing every time I hit F5, independent of browser.
This is the reason
When using cookie-based session state, ASP.NET does not allocate storage for session data until the Session object is used. As a result, a new session ID is generated for each page request until the session object is accessed. If your application requires a static session ID for the entire session, you can either implement the Session_Start method in the application's Global.asax file and store data in the Session object to fix the session ID, or you can use code in another part of your application to explicitly store data in the Session object.
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.sessionid.aspx
So basically, unless you access your session object on the backend, a new sessionId will be generated with each request
EDIT
This code must be added on the file Global.asax. It adds an entry to the Session object so you fix the session until it expires.
protected void Session_Start(Object sender, EventArgs e)
{
Session["init"] = 0;
}
There is another, more insidious reason, why this may occur even when the Session object has been initialized as demonstrated by Cladudio.
In the Web.config, if there is an <httpCookies> entry that is set to requireSSL="true" but you are not actually using HTTPS: for a specific request, then the session cookie is not sent (or maybe not returned, I'm not sure which) which means that you end up with a brand new session for each request.
I found this one the hard way, spending several hours going back and forth between several commits in my source control, until I found what specific change had broken my application.
In my case I figured out that the session cookie had a domain that included www. prefix, while I was requesting page with no www..
Adding www. to the URL immediately fixed the problem. Later I changed cookie's domain to be set to .mysite.com instead of www.mysite.com.
my problem was that we had this set in web.config
<httpCookies httpOnlyCookies="true" requireSSL="true" />
this means that when debugging in non-SSL (the default), the auth cookie would not get sent back to the server. this would mean that the server would send a new auth cookie (with a new session) for every request back to the client.
the fix is to either set requiressl to false in web.config and true in web.release.config or turn on SSL while debugging:
Using Neville's answer (deleting requireSSL = true, in web.config) and slightly modifying Joel Etherton's code, here is the code that should handle a site that runs in both SSL mode and non SSL mode, depending on the user and the page (I am jumping back into code and haven't tested it on SSL yet, but expect it should work - will be too busy later to get back to this, so here it is:
if (HttpContext.Current.Response.Cookies.Count > 0)
{
foreach (string s in HttpContext.Current.Response.Cookies.AllKeys)
{
if (s == FormsAuthentication.FormsCookieName || s.ToLower() == "asp.net_sessionid")
{
HttpContext.Current.Response.Cookies[s].Secure = HttpContext.Current.Request.IsSecureConnection;
}
}
}
Another possibility that causes the SessionID to change between requests, even when Session_OnStart is defined and/or a Session has been initialized, is that the URL hostname contains an invalid character (such as an underscore). I believe this is IE specific (not verified), but if your URL is, say, http://server_name/app, then IE will block all cookies and your session information will not be accessible between requests.
In fact, each request will spin up a separate session on the server, so if your page contains multiple images, script tags, etc., then each of those GET requests will result in a different session on the server.
Further information: http://support.microsoft.com/kb/316112
My issue was with a Microsoft MediaRoom IPTV application. It turns out that MPF MRML applications don't support cookies; changing to use cookieless sessions in the web.config solved my issue
<sessionState cookieless="true" />
Here's a REALLY old article about it:
Cookieless ASP.NET
in my case it was because I was modifying session after redirecting from a gateway in an external application, so because I was using IP instead on localhost in that page url it was actually considered different website with different sessions.
In summary
pay more attention if you are debugging a hosted application on IIS instead of IIS express and mixing your machine http://Ip and http://localhost in various pages
In my case this was happening a lot in my development and test environments. After trying all of the above solutions without any success I found that I was able to fix this problem by deleting all session cookies. The web developer extension makes this very easy to do. I mostly use Firefox for testing and development, but this also happened while testing in Chrome. The fix also worked in Chrome.
I haven't had to do this yet in the production environment and have not received any reports of people not being able to log in. This also only seemed to happen after making the session cookies to be secure. It never happened in the past when they were not secure.
Update: this only started happening after we changed the session cookie to make it secure. I've determined that the exact issue was caused by there being two or more session cookies in the browser with the same path and domain. The one that was always the problem was the one that had an empty or null value. After deleting that particular cookie the issue was resolved. I've also added code in Global.asax.cs Sessin_Start method to check for this empty cookie and if so set it's expiration date to something in the past.
HttpCookieCollection cookies = Response.Cookies;
for (int i = 0; i < cookies.Count; i++)
{
HttpCookie cookie = cookies.Get(i);
if (cookie != null)
{
if ((cookie.Name == "ASP.NET_SessionId" || cookie.Name == "ASP.NET_SessionID") && String.IsNullOrEmpty(cookie.Value))
{
//Try resetting the expiration date of the session cookie to something in the past and/or deleting it.
//Reset the expiration time of the cookie to one hour, one minute and one second in the past
if (Response.Cookies[cookie.Name] != null)
Response.Cookies[cookie.Name].Expires = DateTime.Today.Subtract(new TimeSpan(1, 1, 1));
}
}
}
This was changing for me beginning with .NET 4.7.2 and it was due to the SameSite property on the session cookie. See here for more info: https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
The default value changed to "Lax" and started breaking things. I changed it to "None" and things worked as expected.
Be sure that you do not have a session timeout that is very short, and also make sure that if you are using cookie based sessions that you are accepting the session.
The FireFox webDeveloperToolbar is helpful at times like this as you can see the cookies set for your application.
Session ID resetting may have many causes. However any mentioned above doesn't relate to my problem. So I'll describe it for future reference.
In my case a new session created on each request resulted in infinite redirect loop. The redirect action takes place in OnActionExecuting event.
Also I've been clearing all http headers (also in OnActionExecuting event using Response.ClearHeaders method) in order to prevent caching sites on client side. But that method clears all headers including informations about user's session, and consequently all data in Temp storage (which I was using later in program). So even setting new session in Session_Start event didn't help.
To resolve my problem I ensured not to remove the headers when a redirection occurs.
Hope it helps someone.
I ran into this issue a different way. The controllers that had this attribute [SessionState(SessionStateBehavior.ReadOnly)] were reading from a different session even though I had set a value in the original session upon app startup. I was adding the session value via the _layout.cshtml (maybe not the best idea?)
It was clearly the ReadOnly causing the issue because when I removed the attribute, the original session (and SessionId) would stay in tact. Using Claudio's/Microsoft's solution fixed it.
I'm on .NET Core 2.1 and I'm well aware that the question isn't about Core. Yet the internet is lacking and Google brought me here so hoping to save someone a few hours.
Startup.cs
services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder
.WithOrigins("http://localhost:3000") // important
.AllowCredentials() // important
.AllowAnyMethod()
.AllowAnyHeader(); // obviously just for testing
}));
client.js
const resp = await fetch("https://localhost:5001/api/user", {
method: 'POST',
credentials: 'include', // important
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
Controllers/LoginController.cs
namespace WebServer.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
[HttpPost]
public IEnumerable<string> Post([FromBody]LoginForm lf)
{
string prevUsername = HttpContext.Session.GetString("username");
Console.WriteLine("Previous username: " + prevUsername);
HttpContext.Session.SetString("username", lf.username);
return new string[] { lf.username, lf.password };
}
}
}
Notice that the session writing and reading works, yet no cookies seem to be passed to the browser. At least I couldn't find a "Set-Cookie" header anywhere.

Does FormsAuthentication.SetAuthCookie() Require a Redirect?

After checking a user's credentials and confirming they are good, I'm using FormsAuthentication.SetAuthCookie("Username", false); to authenticate the user.
In the masterpage I then use Page.User.Identity.IsAuthenticated to make sure we're dealing with a logged in user and not a guest.
The problem lies in first setting the auth cookie. When I set the auth cookie, immediately afterwards I run a method that uses Page.User.Identity.IsAuthenticated to change the welcome message from a generic "Welcome, guest!" message to a more personal "Welcome, username!" message. This does not work until I go to another page, so I know the login process has worked, but it seems I cannot access the information I need until a refresh or a redirect happens.
Do I need to redirect the user after setting the auth cookie in order use Page.User.Identity.IsAuthenticated to change the message?
I have seen this before so I know the answer is yes. (As in, yes you do need to redirect the user to correctly use Page.User.Identity.IsAuthenticated)
What I imagine is the cause is because IsAuthenticated evaluates the current request, and when the current request first came in it was recorded as not authenticated.
What you will need to do is apply whatever logic you have in said method without the check for IsAuthenicated (make it assume true).
Now I don't know the details of your method as to suggest how to re-factor it to cope with this, but you could split out the "Do Stuff" part into a separate function which you could then call directly from you login function to bypass the authentication check.
EDIT: To back up my assumption you can read this page.
The interesting part:
The forms-authentication ticket supplies forms-authentication
information to the next request made by the browser.
I'd like to point out that there's actually a way around this (since I've never seen this said in any other question like this). You can retrieve the cookie and its data where User.Identity's information comes from without a redirect. The thing is, the cookie just hasn't been sent to the browser yet.
It simply gets the cookie made by FormsAuthentication from the Response.Cookies object:
HttpCookie EncryptedCookie = Response.Cookies.Get(FormsAuthentication.FormsCookieName);
FormsAuthenticationTicket DecryptedCookie;
try {
DecryptedCookie = FormsAuthentication.Decrypt(EncryptedCookie.Value);
} catch (ArgumentException) {
// Not a valid cookie
return false;
}
// DecryptedCookie.Name: The Username
// DecryptedCookie.UserData: Any additional data, as a string. This isn't normally used
return !DecryptedCookie.Expired;

user is authenticated but Ticket.UserData is missing

I have the following code:
if (HttpContext.Current.Request.IsAuthenticated == false)
{
// this isn't reached so i know user is Authenticated
return;
}
FormsIdentity fIdentity = HttpContext.Current.User.Identity as FormsIdentity;
string[] delimitedUserData = fIdentity.Ticket.UserData.Split('|');
// but at this point delimitedUserData.Length is 0
Any ideas on what would cause the authentication ticket to be valid yet the UserData is gone?
My program usually works just fine and all the UserData is readily accessible. But every once in awhile I get into this state where the UserData is not there.
The ticket is stored in a cookie. What happens in your code when you access a page just after the cookie has expired?
Also note that User.Identity.IsAuthenticated returns true out of the box, so that property is perhaps not the best thing to test on?
By itself, FormsAuthentication doesn't put anything into your UserData. It'd be worth putting a breakpoint near where you handle ticket creation (and the creation of your UserData) and tracing through the path it takes.
As your bug is intermittent, it'll probably be hard to force it to trigger. A place to start could be tracing through how it handles the cookie expiring, or when a cookie is invalid.
If you're using Firefox, I recommend using the "Add N Edit Cookies" plugin: https://addons.mozilla.org/en-US/firefox/addon/573
Not sure if this is the best approach as I'm still fairly new to asp.net, but the way that I do this on the login is to set a session value that I can check in later pages - that way, if the cookie is missing, I should not be able to get the value back so I can transfer to the login page.
So, directly after the login (in the _LoggedIn event), I do:
// write ClientID to the session
Session.Add("ClientID", lClientID);
then on the load of each page behind the login, I do:
if (User.Identity.IsAuthenticated == false || Convert.ToInt32(Session["ClientID"]) == 0)
{
Server.Transfer("Login.aspx");
}
So far, it's worked pretty well for me.
i am searching for the same issue and find the problem that, i used
"FormsAuthentication.RedirectFromLoginPage" to add cookie. i changed into "response.cookies.add". and its working.

Cookie loses value in ASP.net

I have the following code that sets a cookie:
string locale = ((DropDownList)this.LoginUser.FindControl("locale")).SelectedValue;
HttpCookie cookie = new HttpCookie("localization",locale);
cookie.Expires= DateTime.Now.AddYears(1);
Response.Cookies.Set(cookie);
However, when I try to read the cookie, the Value is Null. The cookie exists. I never get past the following if check:
if (Request.Cookies["localization"] != null && !string.IsNullOrEmpty(Request.Cookies["localization"].Value))
Help?
The check is done after a post back? If so you should read the cookie from the Request collection instead.
The cookies are persisted to the browser by adding them to Response.Cookies and are read back from Request.Cookies.
The cookies added to Response can be read only if the page is on the same request.
The most likely answer is seen on this post
When you try to check existence of a cookie using Response object rather than Reqest, ASP.net automatically creates a cookie.
Edit: As a note, I ended up writing software that needed to check the existence of cookies that ASP.NET makes a nightmare due to their cookie API. I ended up writing a conversion process that takes cookies from the request and makes my state object. At the end of the request I then translate my state object back to cookies and stuff them in the response (if needed). This alleviated trying to figure out if cookies are in the response, to update them instead, avoiding creating of pointless cookies etc.
Have you tired "Request" collection instead of "Response" collection?
if (Request.Cookies["localization"] != null && !string.IsNullOrEmpty(Request.Cookies["localization"].Value))
I had a similar problem, I couldn't read cookies on postback. The issue for me was that I checked the Secure property of the cookie to true. It is said that when the Secure property of the cookie is turned on, it causes the cookie to be transmitted only if the connection uses the Secure Sockets Layer. I am not sure, however, how I was able to see the cookie in the browser the first time, but not on postback, considering that I wasn't transmitting over SSL. Anyhow, turning the cookie.Secure to false, solved the problem, and had cookies read on postback.
Sorry if this doesn't have to do anything with your issue, I wanted to share this, because I spent some time looking how to resolve this.
I think I know the answer.
Just REMOVE the action attribute in your <form> tag.
make it look like this: <form id="form1" runat="server">
instead of this: <form id="form1" action="DisplayName.aspx" runat="server">
You should then use Response.Redirect("DisplayName.aspx"); in your code.
if you're compiling in debug mode, turn on tracing for the pages in question and make sure the cookie is in the request collection. Set trace in the #page directive in the aspx file.
Try this snippet -
string locale = ((DropDownList)this.LoginUser.FindControl("locale"))
.SelectedValue;
HttpCookie myCookie = new HttpCookie("localization");
Response.Cookies.Add(myCookie);
myCookie.Values.Add("locale", locale);
Response.Cookies["localization"].Expires = DateTime.Now.AddYears(1);
& to read it -
if (Request.Cookies["localization"] != null)
{
HttpCookie cookie = Request.Cookies["localization"];
string locale = cookie.Values["locale"].ToString();
}
Would add this as a comment to Chris Marisic's answer but I don't have that privelage :-(
Further to what Chris said in his edit about removing cookies from the request, to be able to read the newly created cookies value in a postback I ended up doing
Private Sub SetPageSize(ByVal pageSize As Integer)
' Set cookie value to pageSize
Dim pageSizeCookie As HttpCookie = New HttpCookie(pageSizeCookieName)
With pageSizeCookie
.Expires = Now.AddYears(100)
.Value = pageSize.ToString
End With
' Add to response to save it
Me.Response.Cookies.Add(pageSizeCookie)
' Add to request so available for postback
Me.Request.Cookies.Remove(pageSizeCookieName)
Me.Request.Cookies.Add(pageSizeCookie)
End Sub
The Request.Cookies.Remove and Request.Cookies.Add lines making it work on postbacks
use Response.Cookies.Add(cookie); instead of Response.Cookies.Set(cookie);

Categories

Resources