I am trying to use a session but when i need the value back its null?
My website starts off with a login where i put the user name into a Session.
Session["userName"] = login.UserName;
Then the website is redirected to a new page.
return RedirectToAction("Choice", new { token = token });
after witch i use a link to move to my next page.
#Html.ActionLink("Download", "Download", new {token = Model.Token})
Where i direct my code to a action result in the Home controller, the same controller i have my login function.
#Html.ActionLink("Download", "DownloadFile", new { fileid = item.Key, token = Model.Token, platform = "windows", filename = item.Value }, new {#class = "DownloadLink" } )
Where i try call up my session value again.
formMobiApi.GetFile(token, fileid, platform, filename, Session["userName"].ToString(), tasks, taskId, spreadSheetStatus);
Are any of these actions doing anything to make my session value null?
Can i use a session like this?
If you are not manually setting your session state in web.config then the defaults you should have are in-process (sessions are stored in the RAM of the web server) and timeout is 20 minutes - see here for more information on session states.
You can manually set your states in your web.config file with the following:
<configuration>
<system.web>
<sessionState mode="InProc" timeout="20"></sessionState>
</system.web>
</configuration>
Now, onto your actual issue. I'm not exactly sure why your session isn't persisting and I can't feasibly find out as I don't have a copy of your code to debug. At a guess I'd say your HttpContext is different from when you initialized the session, to when you're requesting it and so your context key is different. Try changing
Session["userName"].ToString();
to
HttpContext.Current.Session["userName"].ToString();
This looks a little odd at first glance, as you're effectively doing the EXACT same thing. The difference comes down to if the current object doesn't have access to the HttpContext you need to explicitly define which context to read from.
Having taken another look at the code you've provided, there's nothing out of the ordinary - this should work. I really am starting to believe this is an environment / configuration issue. If you're using Form Authentication then check your timeout in your web.config file. Also, if you're hosting in IIS (and not Visual Studio's web server - although you should check this too!) try deleting your application pool and recreating it.
Your session variable is set up properly and it is being called properly to obtain the value.
The only thing left to debug is to actually see if login.UserName has an actually value. Because you might just be setting a session variable to null(login.UserName).
also try adding this to your web.config file if that doesn't work.
<sessionState mode="InProc" timeout="20" customProvider="DefaultSessionProvider">
<providers>
<add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
</providers>
</sessionState>
Related
In order to increase session timeout, it appears I would use the following setting:
<system.web>
<sessionState mode="InProc" timeout="20" />
/* Etc... */
</system.web>
Here the timeout is set to 20 minutes (the default value). And, apparently, the maximum value is 525,600 minutes, or one year.
I can come back to Facebook a week later and I'm still logged in. This is how I want my application to behave. But according to this answer, this can adversely affect performance because "your inactive sessions will remain in Web server memory which may cause application pool to recycle, which would result in loosing all sessions for all users."
Does anyone know the details about this performance hit? And, if it's for real, is there a more performant way to keep users logged in like sites such as Facebook?
UPDATE:
Below is the relevant section of my current web.config file.
<system.web>
<authentication mode="None" />
<sessionState mode="InProc" timeout="60" />
<compilation debug="true" targetFramework="4.6" />
<httpRuntime targetFramework="4.5.2" executionTimeout="240" maxRequestLength="20480" />
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
<customErrors mode="Off"></customErrors>
</system.web>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
<remove name="ApplicationInsightsWebTracking" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="20971520" />
</requestFiltering>
</security>
</system.webServer>
UPDATE 2:
Looks like I was incorrectly conflating two issues (authentication and session state). I apologize for not correctly sorting through some issues I was Googling on. My goal is only to extended the length of time a user is logged in.
For Login, you must use FormsAuthentication or ASP.NET Identity (Improved version of cookie based authentication over FormsAuthentication) which allows you to keep authentication cookie for more then weeks/months. FormsAuthentication is stateless, and in order to support multiple servers, you can use single machineKey in all servers. All samples and tutorials mostly guide to use FormsAuthentication by default.
Faceboook and everyone use authentication cookie, no body uses Session for login.
Ideally Session is bad and mostly unnecessary. It can be replaced with HttpRuntime.Cache. Cache can be easily setup to use some external provider such as Fabric cache or Redis. To make cache isolated by user, you can simply append keys of cached item with username.
UPDATE
There is no downside in using FormsAuthentication except that there is little CPU overhead required in decrypting cookie, but that can also be avoided by caching authentication ticket.
The only reason to support Session could be compatibility with old ASP application they might be supporting.
In the new ASP.NET MVC sample, they have configured cookie based authentication in code (in startup), which is not session. Though session is configured in web.config but as long as you don't want to store anything in session, you can completely disable it.
Created a stock MVC project from scratch with Individual User Account selected for Auth.
Startup.Auth.cs
public partial class Startup {
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app) {
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider {
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
ExpireTimeSpan = TimeSpan.FromDays(7)//<-- I just added this.
});
//...code removed for brevity
}
}
// Summary:
// Controls how much time the cookie will remain valid from the point it is
// created. The expiration information is in the protected cookie ticket. Because
// of that an expired cookie will be ignored even if it is passed to the server
// after the browser should have purged it
public TimeSpan ExpireTimeSpan { get; set; }
Changed nothing else in the project and the default template provided everything needed.
UPDATE
Based on comments, you could always add it as a app setting in web.config and use ConfigurationManager to access it. That way it can be modified without having to recompile code.
var expireTimeSpan = TimeSpan.FromDays(7);//the default
var setting = ConfigurationManager.AppSettings["ApplicationCookieExpireTimeInDays"];
if (setting != null) {
var days = 0;
if (int.TryParse(setting, out days)) {
expireTimeSpan = TimeSpan.FromDays(days);
}
}
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider {
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
ExpireTimeSpan = expireTimeSpan
});
Where web.config would hold the setting.
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="ApplicationCookieExpireTimeInDays" value="14" />
</appSettings>
The answer you cite is partially true. It depends on where the session state is stored.
There should be no issue with increasing the session state when storing the session state in the SQL Server database. Also using Web Farms - which makes sense to cater for scalability.
From this article:
Storing Session State in a SQL Server Database
Storing session variables in the SQL server has the following
advantages:
Scalability: If you are looking for a highly scalable option to store
your session variables, the SQL Server option is for you. It is a much
more scalable option than the others. Web farm architecture can very
easily access the session variables because they are stores in an
independent database.
Reliability: Because the data is physically
persisted in a database, it is is more reliable than the other
options. It has the ability to survive server restarts.
Security:
SQL Server is more secure than the in-memory or state server option.
You can protect your data more easily by configuring SQL Server
security.
It's an old article, but these principles still apply.
There can be issues when using memory of the Web Server.
How does increasing the session timeout effect the application performance and why?
If you extend the duration of Sessions, any items held in session
variables will stay in memory on the server longer. Depending on how
busy your application is, and the type and number of items you
persisits as session variables, this may degrade performance.
copied in the typo from the quote.
This question also discusses the difference between session state and the use of cookies with FormsAuthentication.
Should I use Session State or FormAuthentication to keep track of a signed-in user?
So depending on what type of authentication you are using - you may go the cookie route, bearing in mind the user can delete the cookie from the browser and this will log them out.
This is another helpful link to the docs.
Securing Session State
I'm running an Azure Website. Whenever I deploy, everyone gets logged out because the machineKey changes.
I specified the machineKey in the web.config but this didn't solve the issue. I believe this is because Azure automatically overwrites the machineKey [1].
I've found a couple of similar questions here but the answers link to dead links.
So, what's the solution? Surely there's a way to keep users logged in regardless of deployments on Azure.
Try to reset the machine-key configuration section upon Application_Start:
protected void Application_Start()
{
// ...
var mksType = typeof(MachineKeySection);
var mksSection = ConfigurationManager.GetSection("system.web/machineKey") as MachineKeySection;
var resetMethod = mksType.GetMethod("Reset", BindingFlags.NonPublic | BindingFlags.Instance);
var newConfig = new MachineKeySection();
newConfig.ApplicationName = mksSection.ApplicationName;
newConfig.CompatibilityMode = mksSection.CompatibilityMode;
newConfig.DataProtectorType = mksSection.DataProtectorType;
newConfig.Validation = mksSection.Validation;
newConfig.ValidationKey = ConfigurationManager.AppSettings["MK_ValidationKey"];
newConfig.DecryptionKey = ConfigurationManager.AppSettings["MK_DecryptionKey"];
newConfig.Decryption = ConfigurationManager.AppSettings["MK_Decryption"]; // default: AES
newConfig.ValidationAlgorithm = ConfigurationManager.AppSettings["MK_ValidationAlgorithm"]; // default: SHA1
resetMethod.Invoke(mksSection, new object[] { newConfig });
}
The above assumes you set the appropriate values in the <appSettings> section:
<appSettings>
<add key="MK_ValidationKey" value="...08EB13BEC0E42B3F0F06B2C319B..." />
<add key="MK_DecryptionKey" value="...BB72FCE34A7B913DFC414E86BB5..." />
<add key="MK_Decryption" value="AES" />
<add key="MK_ValidationAlgorithm" value="SHA1" />
</appSettings>
But you can load your actual values from any configuration source you like.
If Azure is rewriting your machineKey, you can't do much about it, as it is part of their infrastructure. However, there are other methods.
Override FormsAuthentication
This should not be difficult as you can easily look up for source code of FormsAuthentication and create your own logic and replace MachineKey with your own key stored in web.config or in your database.
Custom Authentication Filter
The simplest way would be to create a filter and check, verify, encrypt decrypt cookies in your filter. You need to do this on OnAuthorization method and create new instance of IPrincipal and set IsAuthenticated to true if descryption was successful.
OAuth
Enable OAuth and create OAuthProvider. However you will need to host OAuthProvider on server that is in your control as that will need machineKey working.
Enable Third Party OAuth, if you enable OAuth with Google, Facebook etc, it will be easy as user will be redirected to OAuth provider and they will continue to login automatically and a new session will be established.
I had the same issue and in my case I was using the webdeploy to Azure wizard in VS13. I thought I was going crazy as I would set the machinekey in the web.config and then it would be changed on the deployed web.config to autogenerate. It is something in the webdeploy script/settings. My solution was to open the live azure site from within VS13 using the Server Explorer and then editing the web.config and saving changes. This preserved my settings with my supplied keys and all works fine.
I get error below,
Session state has created a session id, but cannot save it because the
response was already flushed by the application.
But I dont'use Session any where. My application works as API and It doesn't use session State.
And in Web.config
<system.web>
<sessionState mode="Off"></sessionState>
</system.web>
Why this error going on every request.
I have read that topic. but He uses Session state. but I won't
What's causing “Session state has created a session id, but cannot save it because the response was already flushed by the application.”
Thanks
Try to remove also the session module on web.config as
<httpModules>
<remove name="Session" />
</httpModules>
After that, there is always the possibility some part of your code, or other code that you include to your application, to try to use session -
I have a site that uses the default SqlMembershipProvider and FormsAuthentication. I can use the built-in Login Controls and/or programmatically call all the methods to authenticate a user and get the same result - the user is authenticated and a cookie is created, but the cookie does not appear to be valid since I can't get into any page that requires authentication.
There is no real code to show for the default Login Control since it should just "work", but here is the custom code I tried:
protected void ctrlLogin_Authenticate(object sender, AuthenticateEventArgs e)
{
if (Membership.ValidateUser(ctrlLogin.UserName, ctrlLogin.Password))
{
FormsAuthentication.RedirectFromLoginPage(ctrlLogin.UserName, ctrlLogin.RememberMeSet);
/*
* I also tried this:
FormsAuthentication.SetAuthCookie(ctrlLogin.UserName, ctrlLogin.RememberMeSet);
if (!String.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
Response.Redirect(Request.QueryString["ReturnUrl"]);
Response.Redirect("/index.aspx");
*/
}
else
{
ctrlLogin.FailureText = "Invalid Username/Password Combination";
}
}
With this code, Membership.ValidateUser() succeeds, and both FormsAuthentication.RedirectFromLoginPage() and FormsAuthentication.RedirectFromLoginPage() successfully set a cookie - that cookie just doesn't work to verify my authentication. I have confirmed this by deleting all my cookies and watching them get created again with FireCookie. The cookie name matches what I have in my web.config, the domain is "/", and the expiration date is as expected (see below).
Here are the relevant sections of my web.config:
<authentication mode="Forms">
<forms loginUrl="~/login/index.aspx" name=".SoeAuth" protection="All"
slidingExpiration="true" timeout="525599" domain=""></forms>
</authentication>
<membership defaultProvider="SqlMembershipProvider">
<providers>
<add connectionStringName="[MY_CS]" applicationName="[MY_APPNAME]"
minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0"
enablePasswordReset="true" passwordFormat="Hashed" requiresUniqueEmail="true"
name="SqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider"
requiresQuestionAndAnswer="false"/>
</providers>
</membership>
It should be noted that I also added a machineKey entry in my web.config file based on a suggestion from a very similar question here (which didn't solve my problem). Also, for reference, the timeout=525599 above is 1 minute less than a year for my persistent cookies.
I found the problem:
Since I was able to create a simple working test project with the exact same source code, I determined that the problem was in the web.config file.
Going through each section, I discovered in the 'system.web / httpModules' section I had a <clear/> element. This removed the <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule"/> module defined in machine-level web.config file. Adding it back in instantly fixed the problem.
It sure would have been nice to get an error message when I tried to use the FormsAuthentication methods and that module wasn't even loaded...
Using ASP.NET 2.0, with forms authentication.
Just for a test, I configured the roles cookie in web.config like this :
<roleManager enabled="true" cacheRolesInCookie="true" cookieName=".ASPXROLES" cookieTimeout="2"></roleManager>
I wanted to see what would happen when the cached role cookie expired.
Using Fiddler, after 2 minutes had elapsed, I could see that the raw value of the role cookie had changed.
I was expecting that on expiry, that ASP.NET would simply re-read the roles information from the database, and repopulate the cookie with the same value. So my question is, why would the raw value of the cookie change after expiry ? The cookie value is not human-readable (base 64 encoded and/or encrypted ?), so I can't tell if the information in it is the same, although the application still seems to work fine.
EDIT :
It looks like each time the roles are encrypted and cached in the cookie, it gets a different raw value.
e.g. if you run the following code :
RolePrincipal rp = (RolePrincipal) User;
string str = rp.ToEncryptedTicket();
Label1.Text = str;
You get a different value each time.
So the behavior seems normal.
Well the aspxroles cookie only pertains to role queries on the user. Unless you're doing things with the roles that would cause it to function differently (web.config auth?) then you're not going to see anything by expiring the cookie.
Can you share your web.config and basic pages that you're using to test this?
Have you tried that particular configuration to see what changes after the expiration?
<location path="img/logo.png">
<system.web>
<authorization>
<deny users="?"/>
<allow roles="CanSeeLogo"/>
</authorization>
</system.web>
</location>
Based on the question edit:
In my web.config under <configuration><system.web> I have this key:
<machineKey decryption="AES" decryptionKey="{64bits random hex}" validation="SHA1" validationKey="{128 bits random hex}"/>
I'm curious if you set that "manually" if you'll have a constantly changing encrypted string. Also, this is set by default in your C:\Windows\Microsoft.Net\Framework\etc folders, but you can redefine it (obviously) in your web.config to override it per application. This also allows you to share the same cookie cross-app within your domain.
Link to generate random hex strings
https://www.grc.com/passwords.htm
concat the first result from two page refreshes for the second one. Removing the web.config key later doesn't impact your app negatively (of course it wouldn't)