I'm using FormsAuthentication, I'm having problems setting the TimeOut value.
I've seen some other posts related to this, but they don't seem to be exactly my problem or the solution suggested doesn't help.
My web.config has the following:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn"
timeout="1"
cookieless="UseCookies" />
</authentication>
I have put an AuthorizeAttribute on the controllers that I want to secure.
I can view the .ASPXAUTH cookie (using FireCookies) and I can see that it is set to expire in 1 minute after a login.
If I debug through my code I can see that FormsAuthentication.Timeout = 1.
However my ticket doesn't seem to timeout in 1 minute. After 1 minute of inactivity I can still browse to controllers with AuthorizeAttribute.
In fact I can actually delete the .ASPXAUTH cookie using FireCookies and I can still browse to controllers with an AuthorizeAttribute.
Bizarrely after being inactive for a long time (sorry don't have an exact time - I was out for lunch!) the TimeOut occurs and I am redirected
to the login screen.
Any ideas?
I too had the same problem. Actually, it came about because I could not read the forms authentication cookie from javascript, it was after a while undefined. I just wanted to know if I was authenticated via javascript.
Later I found that the ticket had expired, but I was not getting logged out (also. so i wanted to solve that too)! I saw your question had not been answered so I kept it open while I worked out my problems for half a day. The following is what I came up with that appears to be working.
My Answer is based on this answer. https://stackoverflow.com/a/454639/511438 by user ScottS
This is in my ASP.NET MVC 3 project.
Here is my login code. Not shown, the custom user authentication logic before it. This just sets the initial ticket.
public class FormsAuthenticationService : IFormsAuthentication
public void SignIn(string userName, bool createPersistentCookie, string role)
{
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1, // version
userName, // user name
DateTime.Now, // created
DateTime.Now.Add(FormsAuthentication.Timeout), // expires
false, // rememberMe?
role // can be used to store roles
);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Current.Response.Cookies.Add(authCookie);
}
in the same class but a static method that is accessed from global.asax
//-- this is based on https://stackoverflow.com/questions/454616/asp-net-cookies-authentication-and-session-timeouts
internal static FormsAuthenticationTicket RefreshLoginCookie(bool retainCurrentExpiry)
{
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null || authCookie.Value == null)
return null;
FormsAuthenticationTicket oldTicket = FormsAuthentication.Decrypt(authCookie.Value);
DateTime expiryDate = (retainCurrentExpiry ? oldTicket.Expiration : DateTime.Now.Add(FormsAuthentication.Timeout));
HttpContext.Current.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
var newTicket = new FormsAuthenticationTicket(oldTicket.Version, oldTicket.Name, oldTicket.IssueDate, expiryDate,
oldTicket.IsPersistent, oldTicket.UserData, oldTicket.CookiePath);
HttpCookie newAuthCookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(newTicket));
HttpContext.Current.Response.Cookies.Add(newAuthCookie);
return newTicket;
}
Global.asax
My customization comes that ajax requests do not refresh the forms authentication ticket. So if you sit there for the timeout period, an ajax request will log you out. Change that if you want ajax requests to keep the ticket alive (addresses my javascript cookie issue, not your logout inactivity issue).
*(tip, if you get logged out, then login, but come back to the login page again, the first login did not specify a returnUrl in the querystring perhaps). *
protected virtual void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null || authCookie.Value == "")
{
return;
}
bool isAjax = new HttpRequestWrapper(System.Web.HttpContext.Current.Request).IsAjaxRequest();
FormsAuthenticationTicket authTicket;
try
{
//-- THIS IS WHAT YOU WANT
authTicket = FormsAuthenticationService.RefreshLoginCookie(isAjax);
}
catch
{
return;
}
string[] roles = authTicket.UserData.Split(';');
if (Context.User != null) Context.User = new GenericPrincipal(Context.User.Identity, roles);
}
Web.config
here is the part where i set session timeout and ticket timeout
<configuration>
<system.web>
<sessionState mode="InProc" timeout="60" />
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="60" name="ProviderMvcSession" cookieless="UseCookies" />
</authentication>
An obvious solution that could easily be looked over by some people, clearing the browser cookies or logging out should solve the issue. If remember me is checked, the application will keep logging in with the old cookie regardless the changes made to the Web.Config timeout value.
It's about how long is the timeout of your session. Default is 20 minutes and you can change it in web.config like this:
<sessionState mode="InProc" timeout="20"/>
Related
In my MVC 5 web application when the user session expires and the user clicks in something in the page that does an AJAX call I was still getting response status 200 with X-Responded-JSON:401.
I have then read and investigated and found a way to solve the problem.
So...added to the Startup class ConfigureAuth method this
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider =
new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(TimeSpan.FromMinutes(15), (manager, user) => user.GenerateUserIdentityAsync(manager)),
OnApplyRedirect = ctx =>
{
if (!IsAjaxRequest(ctx.Request))
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
},
SlidingExpiration = false,
ExpireTimeSpan = TimeSpan.FromMinutes(30)
});
Just a helper...
private static bool IsAjaxRequest(IOwinRequest request)
{
IReadableStringCollection query = request.Query;
if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
{
return true;
}
IHeaderDictionary headers = request.Headers;
return (headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest");
}
This solved my problem locally (in my dev machine) and I am now able to see the 401 in the Ajax calls when the sessions expires or the user logs or when is still trying to access something in a open tab.
My problem is that this work only locally. When I have my application in a server this Ajax calls still return 200 and I do not understand why....however if I access the application inside of the box everything works as expected and I can see the 401 in the browser
Can someone maybe know a better solution for this or maybe the reason why this works locally but not in the server?
Many thanks
The solution posted in the initial question works as expected. This approach was not working in the beginning because of the custom error pages that you can define in the web.config. It was messing up with the initial solution that I found in the web.
<system.web>
<globalization enableClientBasedCulture="true" culture="en-GB" uiCulture="auto" />
<customErrors mode="Off" defaultRedirect="~/Error/Index">
<error statusCode="400" redirect="~/Error/Index" />
<error statusCode="401" redirect="~/Error/Index" />
...
...
This was redirecting to a different page and because of that was returning 200 instead of 401.
Thanks
I have the following requirements
Set a value in web.config and enable maintenance mode
All non-ajax requests should be shown a custom error page, with the http status code set to 503. The url of page should be retained.
All ajax requests should be responded with http status code 503
I should have an opportunity to do some basic logging to a file. Log the url and the user Identity if he happened to be logged into the app
I am using ELMAH to track/log all unhandled exceptions. The mechanism for implementing maintenance mode shouldn't need me to not use ELMAH
I have "runAllManagedModulesForAllRequests" set to true. this was originally done for use with RequestReduce. we no longer use it, but I am hesitant to reset its value to false. I am not sure if any other library needs it.
Once I realized there is nothing built in which supports the above requirements, I felt I had the following two options in front of me (App_offile.html won't work for me).
an HttpModule
an MVC ActionFilter
I dropped the MVC ActionFilter as I couldn't figure out how to guarantee it to run before any authentication/authorization filters. I have a custom authentication filter which hits the db. The idea behind the maintenance mode is the db might be offline, yet the web-app shouldn't show a 500 custom error page, but a 503 custom error page.
I wrote the following httpmodule and added in my web.config. It works for ajax requests. It kinda works for non-ajax requests. All requests get redirected to the 503 error page. The side-effect is all requests for static content also result in a 503. My error page thus is shown unstyled :(
// the http module
public class MaintenanceModeModule : IHttpModule
{
private static bool _isUnderMaintenance;
static MaintenanceModeModule()
{
var valueStr = (ConfigurationManager.AppSettings["UnderMaintenance"] ?? (false).ToString());
bool underMaintenance;
bool.TryParse(valueStr, out underMaintenance);
_isUnderMaintenance = underMaintenance;
}
public void Init(HttpApplication application)
{
application.BeginRequest += OnBeginRequest;
}
private void OnBeginRequest(object sender, EventArgs e)
{
var application = (HttpApplication) sender;
var request = application.Request;
var response = application.Response;
if (_isUnderMaintenance == false)
{
return;
}
application.Context.Items["under_maintenance"] = true; // used later
if (request.Url.PathAndQuery == "/503") // the url of the action that renders the custom error page
{
return;
}
const int statusCode = (int) HttpStatusCode.ServiceUnavailable;
const string statusMessage = "Temporarily down for maintenance";
var requestWrapper = new HttpRequestWrapper(request);
if (requestWrapper.IsAjaxRequest())
{
response.Clear();
response.ClearContent();
response.ClearHeaders();
response.StatusCode = statusCode;
response.TrySkipIisCustomErrors = true;
response.StatusDescription = statusMessage;
response.End();
return;
}
// doesn't work, shows the Yellow Screen of Death (YSoD)
// application.Context.Server.Transfer("~/503", preserveForm: true);
// doesn't work, shows the Yellow Screen of Death (YSoD)
// throw new HttpException(statusCode, statusMessage);
response.Redirect("~/503");
}
public void Dispose()
{
}
}
...
// web.config
// only the relevant portions of each section is shown
<appSettings>
<add key="UnderMaintenance" value="true" />
</appSettings>
<customErrors mode="On"> <!-- Custom errors are on, even then I was seeing YSoDs during my attempts -->
<error statusCode="404" redirect="404" />
<error statusCode="503" redirect="503" />
</customErrors>
<system.webServer>
<httpErrors existingResponse="PassThrough">
</httpErrors>
<modules runAllManagedModulesForAllRequests="true">
<add name="MaintenanceMode" type="WebApp.Code.MvcInfrastructure.MaintenanceModeModule" />
</modules>
</system.webServer>
...
// route config
routes.MapRoute("503Error", "503", new { controller = "Error", action = "UnderMaintenance" });
...
// error controller
// the authentication filter skips authentication if the allowanonymous attribute is present
[AllowAnonymous]
public class ErrorController : CustomBaseController
{
public ErrorController(AppConfig appConfig)
: base(appConfig)
{
}
public ActionResult UnderMaintenance()
{
// behind the scenes reads the value from HttpContext.Items.
// This was set during the execution of the httpmodule
if (AppConfig.UnderMaintenance == false)
{
return new RedirectResult("~/");
}
Response.StatusCode = (int) HttpStatusCode.ServiceUnavailable;
Response.TrySkipIisCustomErrors = true;
// the actual content of the view is not relevant now
return View("Error503");
}
}
The problems with this approach,
Each non-ajax request is responded with a 302 and then a 503
The URL requested by the browser is not retained
It returns a 503 for all static assets as well
The code I wrote and web.config settings I enabled are all cobbled together from various sources. I am not fully sure what those settings do or what the recommended way is. Please feel free to answer with a completely different method, as long as it can meet the requirements stated.
I am implementing Anti-Forgery framework as described here:
http://weblogs.asp.net/srkirkland/archive/2010/04/14/guarding-against-csrf-attacks-in-asp-net-mvc2.aspx
Plus, to minimize the coding effort, I did the token insertion part at client side handling form.onsumit and ajaxsend events. Everything works fine – until the session expires.
In my application, I display a popup when the user session gets timed out where the user can re-login and continue without refreshing the current page so that the work-in-progress will be safe. But this doesn't go well with the Anti-CSRF logic. When the user tries to re-login after a timed-out session, this throws a CSRF exception as the cookie (__RequestVerificationToken_Lw__) is already expired and all the future POSTs will be invalid until next page refresh.
Is there any way to set the cookie end time to a future date rather than 'session'? I tried to edit Response.Cookie but that made the cookie invalid.
Any help would be appreciated.
Thanks
At the time of user session out (when displaying a popup) is it possible for you to set the httpcookie with expiry in server side.
I have extracted some code from the microsofts antiforgery token implementation.
internal static string GetAntiForgeryTokenName(string appPath)
{
if (string.IsNullOrEmpty(appPath))
{
return "__RequestVerificationToken";
}
return "__RequestVerificationToken_" + Base64EncodeForCookieName(appPath);
}
private static string Base64EncodeForCookieName(string s)
{
byte[] bytes = Encoding.UTF8.GetBytes(s);
string text = Convert.ToBase64String(bytes);
return text.Replace('+', '.').Replace('/', '-').Replace('=', '_');
}
Below code which set the cookie in server side.
string antiForgeryTokenName = GetAntiForgeryTokenName(HttpContext.Request.ApplicationPath);
HttpCookie httpCookie = HttpContext.Request.Cookies[antiForgeryTokenName];
HttpCookie httpCookie2 = new HttpCookie(antiForgeryTokenName, httpCookie.Value)
{
HttpOnly = true
//// your domain Domain = ,
//// path Path = ,
//// set path Expires =
};
HttpContext.Response.Cookies.Set(httpCookie2);
Please note that I haven't tested this code, just give a try if you dont have any other options.
hi i used form authentication and create ticket and add it to Response it works good.
when i look at created cookies by Firefox tools>pageinfo>security>cookies find that the expire time set currently on cookies .
it works good on local but when i upload it on server (server 2008-iis7) ,the expire time
not works but sets on cookie and always my cookie expires about 10 minus and member will
log out.
has it some specially settings,i look at some example on internet but cant not find anythings.
my authentication codes:
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
username,//username
DateTime.Now,
DateTime.Now.AddMinutes(120),
true,
rollname,
FormsAuthentication.FormsCookiePath);
string hashCookies = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,hashCookies); // Hashed ticket
cookie.Expires = DateTime.Now.AddMinutes(120);
Response.Cookies.Add(cookie);
i used this method in global.asax to check requests authentication:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// look if any security information exists for this request
if (System.Web.HttpContext.Current.User != null)
{
// see if this user is authenticated, any authenticated cookie (ticket) exists for this user
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
// see if the authentication is done using FormsAuthentication
if (System.Web.HttpContext.Current.User.Identity is FormsIdentity)
{
// Get the roles stored for this request from the ticket
// get the identity of the user
FormsIdentity identity = (FormsIdentity)System.Web.HttpContext.Current.User.Identity;
// get the forms authetication ticket of the user
FormsAuthenticationTicket ticket = identity.Ticket;
// get the roles stored as UserData into the ticket
string[] roles = ticket.UserData.Split(',');
// create generic principal and assign it to the current request
System.Web.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(identity, roles);
}
}
}
}
in my web config:
<authentication mode="Forms">
<forms loginUrl="~/Home.aspx" timeout="120" />
</authentication>
I want to make "remember time" on a site something aroun one week. In default mvc 3 application I set this changes:
<forms loginUrl="~/Account/LogOn" timeout="10880" slidingExpiration="true" />
But, it is not enought. After half an hour site forget me. What could be wrong?
Did you set propert timeout for the forms authentication ticket and also the isPersistent parameter should be set as true.
FormsAuthenticationTicket tkt = new FormsAuthenticationTicket
(
1, // version
username, // user name
DateTime.Now, // issue date
expiration, // this should also be set to 10880
rememberMeChecked, // this should be true
null, // additional data
"/" // cookie path
);
For more info see here.