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
Related
Also if i don't give my credentials it gives some configuration file error.
I want AD authentication to take place. But it always picks my username when logged from different systems. How do I resolve it?
Is there any way I can make a user group and assign that to the Identity in DefaultAppPool or do I need to change the code for authentication as it is only taking the user from the Identity mentioned in DefaultAppPool?
The code that I am using to validate the user who logs in is as follows:
string groupName = ConfigurationManager.AppSettings["groupName"].ToString();
string domainName = ConfigurationManager.AppSettings["domain"].ToString();
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx,IdentityType.SamAccountName,System.Security.Principal.WindowsIdentity.GetCurrent().Name);
if (user != null)
{ //code as required }
code in web.config file:
<appSettings>
<add key="domain" value="EYDEV" />
<add key="groupName" value="Domain Users" />
</appSettings>
WindowsIdentity.GetCurrent() gets the credentials that your application is running under, not the current user.
You need to first enable Windows Authentication by adding this to your system.web section of your web.config:
<authentication mode="Windows" />
See Microsoft's documentation for more details (like making sure you have the right IIS features installed): https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/security/authenticating-users-with-windows-authentication-cs
Once that is setup, you can use User.Identity.Name in a Controller to get the username of the current user.
I have set Idle-timeout to 1 minutes for the purpose of testing. In addition, I have a SessionState timeout is set to 3 minutes. SessionState timeout is working just fine, but IIS Idle-timeout is not working?
For your information, I have checked the file ApplicationHost.config, the setting is already there
<system.applicationHost>
<applicationPools>
<add name="dev_web_core" autoStart="true" startMode="AlwaysRunning">
<processModel idleTimeout="00:01:00" />
<recycling>
<periodicRestart time="00:00:00">
<schedule>
<clear />
<add value="01:00:00" />
</schedule>
</periodicRestart>
</recycling>
</add>
</applicationPools>
</system.applicationHost>
Based on your comment, it seems you expect the application pool idleTimeout to cause an authentication timeout. This is unrelated. This timeout triggers application pool recycling, which causes your application process to get stopped (and renewed if requests are coming).
Issued authentication tickets are unaffected by this process recycling (at least with frameworks like Asp.Net Identity, provided your site machine key does not change).
You should seek and setup the timeout of your authentication framework instead. With Owin by example, this can be setup when issuing the ticket.
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
// Your custom expiration, preferably taken from some settings
var expiration = rememberMe ? 144000 : 30;
var now = DateTimeOffset.UtcNow;
authenticationManager.SignIn(
new AuthenticationProperties
{
AllowRefresh = true,
IssuedUtc = now,
ExpiresUtc = now.AddMinutes(expiration),
IsPersistent = rememberMe
}, claimsIdentity);
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 have a Windows Azure WebAPI application with
OWIN
Oauth 2
token authentication
Identity 2 Framework
VS2013 Update 2
IIS (I am not sure which version. As I just updated everything in the stack then I assume the latest version)
Before a user is authenticated when there's a <script> in my index.html file I notice cookies sent to the server looking like this:
Accept-Language: en-US,en;q=0.8
Cookie: .AspNet.Cookies=GWLL4LgeFkn7jDndAwf-Pk_eZAPZ5LYZugSmv- ...
After a user is authenticated I notice the cookies change:
Accept-Language: en-US,en;q=0.8
Cookie: .AspNet.Cookies=OqLMSpIv2aQ8KUcw3pWdAYtPYUI_tYMl4rEYKe16N ...
I thought I was using token authentication so my first question is "why do the cookies get changed and why are they sent at all"?
Once a user is authenticated then with each $http request to the server I send a header like this:
Authorization: Bearer abcdefgetc....
My authorization on the server works when I have WebAPI methods decorated like:
[Authorize(Roles = "Admin")]
Here is the main web-config that shows the security settings:
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5.1" />
<httpRuntime targetFramework="4.5.1" />
</system.web>
<system.webServer>
<modules>
<remove name="FormsAuthenticationModule" />
</modules>
</system.webServer>
Now I would like to add some security to some static javascript files that I have on the server. I know how I can code it so the files can be retrieved by my client and added to the DOM in two ways. Either way is okay for me to use although I prefer the first way if when I do that way then there can be authentication happen through cookies or otherwise:
With a script tag and a load
var el = doc.createElement("script"),
loaded = false;
el.onload = el.onreadystatechange = function () {
if ((el.readyState && el.readyState !== "complete" && el.readyState !== "loaded") || loaded) {
return false;
}
el.onload = el.onreadystatechange = null;
loaded = true;
// done!
};
el.async = true;
el.src = path;
document.getElementsByTagName('head')[0].insertBefore(el, head.firstChild);
With a $http call and then adding it directly to the DOM (I can supply bearer token)
$http({
url: '/Bundles/admin/admin1Bundle.js',
method: "GET"
})
.success(function (result) {
var m = document.createElement('script');
m.appendChild(document.createTextNode(result));
document.getElementsByTagName('head')[0].appendChild(m);
Once added the javascript becomes available. To add security I created a web.config in the folder to protect these files and allow only users with Admin role to have access:
Here is the folders web-config that shows the security settings:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<security>
<authorization>
<remove users="*" roles="" verbs="" />
<add accessType="Allow" roles="Admin" verbs='GET'/>
</authorization>
</security>
</system.webServer>
</configuration>
When a user with the role of Admin tries to access files in the folder with a GET that a has a bearer token they get a message saying:
Most likely causes:
•No authentication protocol (including anonymous) is selected in IIS.
•Only integrated authentication is enabled, and a client browser was used that does not support integrated authentication.
•Integrated authentication is enabled and the request was sent through a proxy that changed the authentication headers before they reach the Web server.
•The Web server is not configured for anonymous access and a required authorization header was not received.
•The "configuration/system.webServer/authorization" configuration section may be explicitly denying the user access.
This error occurs when the WWW-Authenticate header sent to the Web server is not supported by the
server configuration. Check the authentication method for the resource, and verify which authentication
method the client used. The error occurs when the authentication methods are different. To determine
which type of authentication the client is using, check the authentication settings for the client.
It seems like my IIS (the version I am using on my development environment when I click Debug > Start Debugging) is using a different kind of authentication from that used by my WebAPI. Can someone explain to me:
Should I be using <system.web> or <system.webServer> for the security?
How can I make the IIS use the same security path as WebAPI is using when I decorate my WebAPI methods? Note that I need a way to do this with web.config as I don't have access to make changes to the IIS directly once the application is published to the cloud.
I am using token authentication so why is the cookie information sent? Could I just use this cookie information to secure my javascript files from getting downloaded?
Notes:
Here is the way I have authentication set up in Startup.Auth.cs
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
Should I be using or for the security?
How can I make the IIS use the same security path as WebAPI is using
when I decorate my WebAPI methods? Note that I need a way to do this
with web.config as I don't have access to make changes to the IIS
directly once the application is published to the cloud.
You could use the authorization attribute in the webconfig to restrict files and folders, the example below restricts restricts a specific js file to admins only.
<location path="resources/scripts/yourtopsecretjsfile.js">
<system.webServer>
<security>
<authorization>
<remove users="*" roles="" verbs="" />
<add accessType="Allow" roles="Administrators" />
</authorization>
</security>
</system.webServer>
</location>
I am using token authentication so why is the cookie information sent?
It is used by the server to identify the authenticated user. If you don't want to do without sending the cookie info you can look at doing at sending a signed token with every request instead. Check out this article, loosely covers how to do that (but that with an angular JS/ Web API 2 point of view) http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
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>