Session expiration issue in ASP.NET MVC - asp.net

Hi I have some issues with ASP.NET MVC session state which is not expiring after I implement the following piece of code and put the attributes over the methods.
public sealed class SessionActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
//Check if session is supported
if (ctx.Session != null)
{
//Check if the session is new
if (ctx.Session.IsNewSession)
{
//If it says it is a new session but an existing cookie exists
//then it must have timed out
string sessionCookie = filterContext.HttpContext.Request.Headers["Cookie"];
if ((sessionCookie != null) && (sessionCookie.IndexOf("ASP.NET_SessionId", StringComparison.OrdinalIgnoreCase) >= 0))
{
//Redirect to the login page
ctx.Response.Redirect("~/Home/Index", true);
ctx.Response.End();
}
}
}
base.OnActionExecuting(filterContext);
}
}
The issue is that the Redirection request is not executing and the Action which has SessionActionFilter Attribute executes. This method uses session variables which are expired and results in error.
Can anybody tell what I am missing?
Thanks a lot in advance!!

We are storing some data which is used in our views!! One more update I was able to run this piece of code which is now working fine. However, I am a bit skeptical about the use of cookies and need to transform this code to work for cookieless sessions also. How is this possible?

Related

Why HttpContext.Current.Session is null in Global.asax?

I'm using VS2010 and created a simple asp. web forms application, using Development Server to test it.
I try to store user data - queried from sql server - in the session, since I don't want to access database in every request. I'm using the 'Application_AuthenticateRequest' and the 'Session_Start' methods.
First round:
AuthenticateRequest called. The following code ran:
public static void Initialize(string login_name, bool force_refresh)
{
HttpSessionState Session = HttpContext.Current.Session;
object o = Session == null ? null : Session["EMPLOYEE_DATA"];
if (force_refresh || o == null || o.GetType() != typeof(Employee) || (o as Employee).login_name!= login_name)
{
_current = UIManager.GetEmployee(login_name);
if (Session != null)
{
Session["EMPLOYEE_DATA"] = _current;
}
}
else
{
_current = (Employee)o;
}
}
The _current variable is a private static field published through a static property.
In the first round the Session is null, and I think it's ok because the Session_Start not called yet.
The Session_Start looks like this:
protected void Session_Start(object sender, EventArgs e)
{
Session["EMPLOYEE_DATA"] = EmployeeFactory.Current;
}
In the next round the Session_Start is not called of course but in the AuthenticateRequest I can't access to the session. The HttpContext.Current.Session is null and the this.Session reference throw a HttpException says the "Session state is not available in this context".
However I can access the Session from any of the page_load events but it's a bad practice I think that I put authentication every page_load.
Any idea how can I access to the Session?
Thanks for advice,
Péter
You're not able to use Session on the Application_AuthenticateRequest becauase it's not bound at that moment.
I think you're able to use the event Application_AcquireRequestState.
try to use the below code in page_Load
Response.AppendHeader("Refresh", Convert.ToString(Session.Timeout * 15) + ";
URL=SessionExpPage.aspx");

ASP.NET Session in Global.asax

Session in Application_AuthenticateRequest method in Global.asax is always null.Ive already try:
this.Session,HttpContext.Current.Session
always null.
protected void Application_AuthenticateRequest()
{
string userRole = string.Empty;
if (Request.IsAuthenticated)
{
if (this.Session["UserRole"] == null)
{
InsertSessionValue();
}
userRole =Session["UserRole"].ToString();
HttpContext.Current.User = new GenericPrincipal(User.Identity, new string[] {userRole});
}
}
Ive also try to use Cache,but it doesnt work because i need unique information for each user.
How to use Session in Global.asax?Is HttpApplication Application property unique for each user?
You just can't use Session at this point in the request lifecycle, it isn't available/populated yet, if you want to use it you'll need to move to an event later in the lifecycle, for example PostAcquireRequestState.

Forms authentication: disable redirect to the login page

I have an application that uses ASP.NET Forms Authentication. For the most part, it's working great, but I'm trying to add support for a simple API via an .ashx file. I want the ashx file to have optional authentication (i.e. if you don't supply an Authentication header, then it just works anonymously). But, depending on what you do, I want to require authentication under certain conditions.
I thought it would be a simple matter of responding with status code 401 if the required authentication was not supplied, but it seems like the Forms Authentcation module is intercepting that and responding with a redirect to the login page instead. What I mean is, if my ProcessRequest method looks like this:
public void ProcessRequest(HttpContext context)
{
Response.StatusCode = 401;
Response.StatusDescription = "Authentication required";
}
Then instead of getting a 401 error code on the client, like I expect, I'm actually getting a 302 redirect to the login page.
For nornal HTTP traffic, I can see how that would be useful, but for my API page, I want the 401 to go through unmodified so that the client-side caller can respond to it programmatically instead.
Is there any way to do that?
ASP.NET 4.5 added the Boolean HttpResponse.SuppressFormsAuthenticationRedirect property.
public void ProcessRequest(HttpContext context)
{
Response.StatusCode = 401;
Response.StatusDescription = "Authentication required";
Response.SuppressFormsAuthenticationRedirect = true;
}
After a bit of investigation, it looks like the FormsAuthenticationModule adds a handler for the HttpApplicationContext.EndRequest event. In it's handler, it checks for a 401 status code and basically does a Response.Redirect(loginUrl) instead. As far as I can tell, there's no way to override this behaviour if want to use FormsAuthenticationModule.
The way I ended up getting around it was by disabling the FormsAuthenticationModule in the web.config like so:
<authentication mode="None" />
And then implementing the Application_AuthenticateEvent myself:
void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.User == null)
{
var oldTicket = ExtractTicketFromCookie(Context, FormsAuthentication.FormsCookieName);
if (oldTicket != null && !oldTicket.Expired)
{
var ticket = oldTicket;
if (FormsAuthentication.SlidingExpiration)
{
ticket = FormsAuthentication.RenewTicketIfOld(oldTicket);
if (ticket == null)
return;
}
Context.User = new GenericPrincipal(new FormsIdentity(ticket), new string[0]);
if (ticket != oldTicket)
{
// update the cookie since we've refreshed the ticket
string cookieValue = FormsAuthentication.Encrypt(ticket);
var cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName] ??
new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue) { Path = ticket.CookiePath };
if (ticket.IsPersistent)
cookie.Expires = ticket.Expiration;
cookie.Value = cookieValue;
cookie.Secure = FormsAuthentication.RequireSSL;
cookie.HttpOnly = true;
if (FormsAuthentication.CookieDomain != null)
cookie.Domain = FormsAuthentication.CookieDomain;
Context.Response.Cookies.Remove(cookie.Name);
Context.Response.Cookies.Add(cookie);
}
}
}
}
private static FormsAuthenticationTicket ExtractTicketFromCookie(HttpContext context, string name)
{
FormsAuthenticationTicket ticket = null;
string encryptedTicket = null;
var cookie = context.Request.Cookies[name];
if (cookie != null)
{
encryptedTicket = cookie.Value;
}
if (!string.IsNullOrEmpty(encryptedTicket))
{
try
{
ticket = FormsAuthentication.Decrypt(encryptedTicket);
}
catch
{
context.Request.Cookies.Remove(name);
}
if (ticket != null && !ticket.Expired)
{
return ticket;
}
// if the ticket is expired then remove it
context.Request.Cookies.Remove(name);
return null;
}
}
It's actually slightly more complicated than that, but I basically got the code by looking at the implementation of FormsAuthenticationModule in reflector. My implementation is different to the built-in FormsAuthenticationModule in that it doesn't do anything if you respond with a 401 - no redirecting to the login page at all. I guess if that ever becomes a requirement, I can put an item in the context to disable the auto-redirect or something.
I'm not sure if this will work for everyone, but in IIS7 you can call Response.End() after you've set the status code and description. This way, that #&$^##*! FormsAuthenticationModule won't do a redirect.
public void ProcessRequest(HttpContext context) {
Response.StatusCode = 401;
Response.StatusDescription = "Authentication required";
Response.End();
}
To build on zacharydl's answer slightly, I used this to solve my woes. On every request, at the beginning, if it's AJAX, immediately suppress the behavior.
protected void Application_BeginRequest()
{
HttpRequestBase request = new HttpRequestWrapper(Context.Request);
if (request.IsAjaxRequest())
{
Context.Response.SuppressFormsAuthenticationRedirect = true;
}
}
I don't know how that Response.End() worked for you. I tried it with no joy, then looked at MSDN for Response.End(): 'stops execution of the page, and raises the EndRequest event'.
For what it's worth my hack was:
_response.StatusCode = 401;
_context.Items["401Override"] = true;
_response.End();
Then in Global.cs add an EndRequest handler (which will get called after Authentication HTTPModule):
protected void Application_EndRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Items["401Override"] != null)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.StatusCode = 401;
}
}
what you've found out is correct about the forms auth intercepting the 401 and doing a redirect but we also can do that to reverse that.
Basically what you need is an http module to intercept the 302 redirect to the login page and reverse it to a 401.
Steps on doing that is explained in here
The given link is about a WCF service but it is the same in all the forms auth scenarios.
As explained in the above link you need to clear the http headers as well but remember to put the cookie header back to the response if the original response (i.e. before intercepting) contained any cookies.
I know there is already an answer with tick but while trying to solve similar problem I found this (http://blog.inedo.com/2010/10/12/http-418-im-a-teapot-finally-a-%e2%80%9clegitimate%e2%80%9d-use/) as an alternative.
Basically you return your own HTTP status code (e.g. 418) in your code. In my case a WCF data service.
throw new DataServiceException(418, "401 Unauthorized");
Then use a HTTP module to handle it at the EndRequest event to rewrite the code back to 401.
HttpApplication app = (HttpApplication)sender;
if (app.Context.Response.StatusCode == 418)
{
app.Context.Response.StatusCode = 401;
}
The browser / client will receive the correct content and status code, it works great for me :)
If you are interested to learn more about HTTP status code 418 see this question & answer.
That's a known issue, and there's a NuGet Package for that and/or the source code available.
Funny hack if you use.NET Framework >= v4.0 but < v4.5. It uses reflection to set value of inaccessible SuppressFormsAuthenticationRedirect property:
// Set property to "true" using reflection
Response
.GetType()
.GetProperty("SuppressFormsAuthenticationRedirect")
.SetValue(Response, true, null);
You do not set the WWW-Authenticate header in the code you show, so the client cannot do HTTP authentication instead of forms authentication. If this is the case, you should use 403 instead of 401, which will not be intercepted by the FormsAuthenticaitonModule.
I had the problem that I wanted to avoid not only the redirect but also the forms authentication itself in order to make a web api work. Entries in web.config with a location tag for the api didn't help.
Thus I used SuppressFormAuthenticationRedirect and HttpContext.Current.SkipAuthorization to suppress the authentication in general.
In order to identify the sender I used e.g. the UserAgent in the Header but it is of course recommendable to implement further authentification steps, e.g. check against the sending IP or send another key with the request.
Below is inserted in the Global.asax.cs.
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Request.UserAgent == "SECRET-AGENT")
{
AppLog.Log("Redirect suppressed");
HttpApplication context = (HttpApplication)sender;
context.Response.SuppressFormsAuthenticationRedirect = true;
HttpContext.Current.SkipAuthorization = true;
}
}
In order to redirect the user to Unauthorize Page rather than to the login page, the Hack is to implement Application_EndRequest in Global and check for Response Status Code 302, which is a temporary redirect from the current called to action.
protected void Application_EndRequest(object sender, EventArgs e)
{
if(HttpContext.Current.Response.StatusCode == 302 && User.Identity.IsAuthenticated)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Redirect("/UnauthorizedPageUrl");
}
}
Look inside your Web.config file in configuration\authentication.
If there is a forms subelement there with a loginUrl attribute, remove it and try again.

ASP.NET - Get SessionID while in from the Global.ASAX

I'm recording the session start times from when people log into my .NET 2.0 web application, but I'd also like to record the Session ID. Can someone give me some example code on how to accomplish this (how to access the Session ID from within the Global.ASAX).
If you need any additional info just let me know.
HttpContext.Current.Session.SessionID
Edit to show null test:
if ((HttpContext.Current != null) && (HttpContext.Current.Session != null) {
id = HttpContext.Current.Session.SessionID
}
You can get at it quite simply with HttpContext.Current.Session.SessionId as you probably already know. You need to be on or after Application_AcquireRequestState before the session state has been loaded, and session state is also only loaded when the requested resource implements IRequiresSessionState. You can see a list of all the events in global.asax here: https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5771721.html and read more about IRequiresSessionState here: http://msdn.microsoft.com/en-us/library/system.web.sessionstate.irequiressessionstate.aspx
Write to the session the datetime and sessionid at the moment of the first request following ASP.NET's identifying the user's session.
protected void Application_PreRequestHandlerExecute(object sender, EventArgs eventArgs) {
var session = HttpContext.Current.Session;
if (session != null) {
if (session["foo"] == null) {
session["foo"] = DateTime.Now.Ticks + "|" + session.SessionID;
}
}
}

ASP.NET 2.0: Problem in Httpcontext.current.session.add()

Can anybody help me to find out solution of following problem.
In ASP.NET website: at Application_OnPostAuthenticate() event, whatever code i write is executed for every request. therefore due to this customidentity object, countryid and weatherid is called everytime for each request (call for database for value). It effect response time of page and unneccessary code execute.
void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
// Get a reference to the current User
IPrincipal objIPrincipal = HttpContext.Current.User;
// If we are dealing with an authenticated forms authentication request
if ((objIPrincipal.Identity.IsAuthenticated) && (objIPrincipal.Identity.AuthenticationType == "Forms"))
{
CustomPrincipal objCustomPrincipal = new CustomPrincipal();
objCustomPrincipal = objCustomPrincipal.GetCustomPrincipalObject(objIPrincipal.Identity.Name);
HttpContext.Current.User = objCustomPrincipal;
CustomIdentity ci = (CustomIdentity)objCustomPrincipal.Identity;
HttpContext.Current.Cache["CountryID"] = FatchMasterInfo.GetCountryID(ci.CultureId);
HttpContext.Current.Cache["WeatherLocationID"] = FatchMasterInfo.GetWeatherLocationId(ci.UserId);
Thread.CurrentPrincipal = objCustomPrincipal;
}
}
To solve this problem when i try tochange code as follows
HttpContext.Current.Session.Add("test", FatchMasterInfo.GetWeatherLocationId(ci.UserId);); in place of cache i found foolowing error
"Object refrence not set to the instance of object"
I don't know whether we can store session variable inside Application_OnPostAuthenticate() event or not?
You could try doing this a bit later in the request, such as in the PreRequestHandlerExecute event:
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
IPrincipal objIPrincipal = HttpContext.Current.User;
if ((objIPrincipal.Identity.IsAuthenticated) && (objIPrincipal.Identity.AuthenticationType == "Forms"))
{
HttpSessionState session = HttpContext.Current.Session;
CustomPrincipal objCustomPrincipal = new CustomPrincipal();
if (session[objIPrincipal.Identity.Name] == null)
{
// get data from database or wherever
objCustomPrincipal = objCustomPrincipal.GetCustomPrincipalObject(objIPrincipal.Identity.Name);
CustomIdentity ci = (CustomIdentity)objCustomPrincipal.Identity;
Object countryID = FatchMasterInfo.GetCountryID(ci.CultureId);
Object weatherLocationID = FatchMasterInfo.GetWeatherLocationId(ci.UserId);
// save in session (not cache as cache is application-wide, not per-user):
session.Add(objIPrincipal.Identity.Name, objCustomPrincipal);
session.Add(objIPrincipal.Identity.Name + "_CountryID", countryID);
session.Add(objIPrincipal.Identity.Name + "_WeatherLocationID", weatherLocationID);
}
else
{
// already have custom principal object in session
objCustomPrincipal = (CustomPrincipal)session[objIPrincipal.Identity.Name];
}
// set the custom principal object to context/thread
HttpContext.Current.User = objCustomPrincipal;
Thread.CurrentPrincipal = objCustomPrincipal;
}
}
You probably don't want to access the session in any event that happens in every request. Some requests don't even have session (for instance, a lot of web service calls, or calls to WebResource.axd that load static resources).
Before adding value to cache object, check if it already exists in the cache.
You might not have session state enabled. Does it work anywhere else (like in a web form's display)?
Look for a <sessionState> element under your system.web element in web.config make sure it's turned on (set it to InProc unless you have a web farm).

Resources