ASP.NET MVC 4 Session expiration - asp.net

I detect session expiration with:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (null != filterContext.HttpContext.Session)
{
// Check if we have a new session
if (filterContext.HttpContext.Session.IsNewSession)
{
string cookie = filterContext.HttpContext.Request.Headers["Cookie"];
// Check if session has timed out
if ((null != cookie) && (cookie.IndexOf("ASP.NET_SessionId") >= 0))
{
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", "SessionExpired");
redirectTargetDictionary.Add("controller", "Error");
redirectTargetDictionary.Add("timeout", "true");
filterContext.Result = new RedirectToRouteResult(redirectTargetDictionary);
return;
}
}
else { //continue with action as usual
base.OnActionExecuting(filterContext);
}
}
}
The problem is that every time I run the application, the SessionExpired action is fired (it finds the ASP.NET_SessionId string in the cookie when I request the application url for the first time), then when I load another url the application works OK. How to fix that?

Related

ASP MVC change cookie value of the user that don't send any request

Supposed we have two users :
user 1, user 2
I want when user 1 send a request for change cookie value, the cookie value of user 2 change.
Here is my code in a class:
public static void changeCookieCount(Int64 targetUserId)
{
int a = //get cookie value from database via `targetUserId`
HttpCookie cookieCount = HttpApp.RequestCookie("cookieCount");
if (cookieCount != null)
{
cookieCount.Value = a;
cookieCount.Expires = DateTime.Now.AddSeconds(5);
cookieCount.Expires = DateTime.Now.AddMonths(2);
cookieCount.Secure = true;
HttpApp.ResponseCookie(cookieCount);
}
else
{
cookieCount = new HttpCookie("cookieCount");
cookieCount.Value = a;
cookieCount.Expires = DateTime.Now.AddSeconds(5);
cookieCount.Expires = DateTime.Now.AddMonths(2);
cookieCount.Secure = true;
HttpApp.ResponseCookie(cookieCount);
}
}
As you see i change cookie value in a class with user HttpApp and this is part of 'HttpApp` :
public class HttpApp: HttpApplication
{
public static void ResponseCookie(HttpCookie cookie)
{
HttpContext.Current.Response.Cookies.Add(cookie);
}
public static HttpCookie RequestCookie(string name)
{
return HttpContext.Current.Request.Cookies[name];
}
}
My question is, how can access target user for change the cookie value?
Store user_1's cookie change request in DB. When user_2 comes back check the db and write the new cookie value to user_2.
Create a table which stores the userId and the new cookie value for this user. Insert new values for user_2 whenever user_1 wants a change request.
Create a ActionFilterAttribute which will be executed on each request:
public class EachRequestFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Session == null)
return;
if (filterContext.HttpContext.Session["UserId"] == null)
return;
// Check database for any cookie change for this UserId and if there is a change update cookie value...
}
}

Basic authentication IHttpModule unexpected behaviour

I have enabled Basic Authentication in IIS7 for my site and followed this link to create handler for basic authentication requests.
The problem is that no matter what credentials user enters, the site keeps returning 401, even if entering correct credentials. This is just a test and credentials are checked against hardcoded values.
Here is relevant code:
public class BasicAuthenticationHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest+=context_BeginRequest;
context.AuthenticateRequest += context_AuthenticateRequest;
}
void context_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
TryAuthenticate(application);
}
private void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
TryAuthenticate(application);
}
private static void TryAuthenticate(HttpApplication application)
{
if (!Authenticate(application.Context))
{
application.Context.Response.Status = "401 Unauthorized";
application.Context.Response.StatusCode = 401;
application.Context.Response.AddHeader("WWW-Authenticate", "Basic");
application.CompleteRequest();
}
}
private static bool Authenticate(HttpContext context)
{
if (context.User!=null && context.User.Identity.IsAuthenticated)
{
return true;
}
if (!context.Request.Headers.AllKeys.Contains("Authorization"))
return false;
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
IPrincipal principal;
if (TryGetPrincipal(authHeader, out principal))
{
context.User = principal;
return true;
}
return false;
}
private static bool TryGetPrincipal(string[] creds, out IPrincipal principal)
{
if (creds[0] == "Administrator" && creds[1] == "SecurePassword")
{
principal = new GenericPrincipal(
new GenericIdentity("Administrator"),
new string[] { "Administrator", "User" }
);
return true;
}
if (creds[0] == "BasicUser" && creds[1] == "Password")
{
principal = new GenericPrincipal(
new GenericIdentity("BasicUser"),
new string[] { "User", "SystemUser" }
);
return true;
}
else
{
principal = null;
return false;
}
}
When client enters correct credentials (i.e. "BasicUser", "Password"), GenericPrincipal object is created and assigned to HttpContext's User property. Looking into Request.IsAuthenticated tells that it's true.
And this is why I don't understand is why client receives 401 again and again and again.
I'm not sure how all the pipeline works - may be basic authentication goes further to some IIS HttpModule which also serves the request? Or may be code is incomplete and context_BeginRequest needs to be extended? (I know that in case of Forms authentication type, you do something like Response.Redirect(goodguy.aspx))
Anyway, any help/questions are appreciated.
Forgot to mention that in web.config I also placed
<system.webServer>
<modules>
<add name="BasicAuthenticationHttpModule" type="Analytics.BasicAuthenticationHttpModule" />
</modules>
</system.webServer>
Apparently implemements it's own Basic authentication. Thus our module could authenticate request succussfully, it would get passed to built-in IIS module, which rejected authentication. It is really helpful not to copy paste, but also to think yourself indeed. So to answer my question - disable all authentication on IIS except Anonymous.

Pass Authenticated Session In Another Request in ASP.NET

I am using ABCPDF.net to render my HTML to PDF pages, but I want to protect these using the current authenticated session. However, the request to get the HTML to render the PDF doesnt carry over the session, but creates a new one. I am using ASP.NET Membership, and passwigin the session ID and creating a cookie on the ASP.NET request.
var sessionId = HttpUtility.ParseQueryString(Url)["sid"];
if(sessionId != null)
{
doc.HtmlOptions.HttpAdditionalHeaders = string.Format("Cookie:
ASP.NET_SessionId={0}", sessionId);
}
I read this in ABCPDF.net documentation and am doing just that, but the request always uses a different session.
httpadditionalheaders
Do I need to pass something else, or can I do something else?
Fixed it by sending the auth cookie in the URL, and updating the cookie.
Url To Call
Url.ActionAbsolute(MVC.Events.Pools(eventId).AddRouteValue(FormsAuthentication.FormsCookieName, Request.Cookies[FormsAuthentication.FormsCookieName].Value)
Global.asax
protected void Application_BeginRequest()
{
try
{
string auth_cookie_name = FormsAuthentication.FormsCookieName;
if (HttpContext.Current.Request.Form[FormsAuthentication.FormsCookieName] != null)
{
UpdateCookie(auth_cookie_name, HttpContext.Current.Request.Form[FormsAuthentication.FormsCookieName]);
}
else if (HttpContext.Current.Request.QueryString[FormsAuthentication.FormsCookieName] != null)
{
UpdateCookie(auth_cookie_name, HttpContext.Current.Request.QueryString[FormsAuthentication.FormsCookieName]);
}
}
catch
{
}
}
void UpdateCookie(string cookieName, string cookieValue)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(cookieName);
if (cookie == null)
{
cookie = new HttpCookie(cookieName);
HttpContext.Current.Request.Cookies.Add(cookie);
}
cookie.Value = cookieValue;
HttpContext.Current.Request.Cookies.Set(cookie);
}

Forcing .net Login control to make the user logout if a cookie is null

I have a code base web application that is connected to 2 databases. Depending on which login control a user uses to login, a different database is connected to the code. I am doing all of this by a cookie. This cookie is in a public class called AuthenticatedUser. The class looks like this:
public class AuthenticatedUser : System.Web.UI.Page
{
public static string ConnectionString
{
get
{
HttpCookie myCookie = HttpContext.Current.Request.Cookies["connectionString"];
return GetConnectionStringFromName(myCookie);
}
set
{
if (HttpContext.Current.Request.Cookies["connectionString"] != null)
{
ExpireCookies(HttpContext.Current);
}
var allCookies = HttpContext.Current.Request.Cookies.AllKeys;
HttpCookie cookie = new HttpCookie("connectionString");
cookie.Value = value;
cookie.Expires = DateTime.Now.AddYears(100);
HttpContext.Current.Response.Cookies.Add(cookie);
}
}
private static string GetConnectionStringFromName(HttpCookie myCookie)
{
try
{
string connectionStringName = myCookie.Value;
return ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
}
catch
{
FormsAuthentication.SignOut();
}
finally
{
HttpContext.Current.Response.Redirect("/default.aspx");
}
return "";
} private static void ExpireCookies(HttpContext current)
{
var allCookies = current.Request.Cookies.AllKeys;
foreach (var cook in allCookies.Select(c => current.Response.Cookies[c]).Where(cook => cook != null))
{
cook.Value = "";
cook.Expires = DateTime.Now.AddDays(-1);
current.Request.Cookies.Remove(cook.Name);
cook.Name = "";
}
}
}
This seems to be working on my development machine, but when I tried to deploy it, any user that was using the "remember me" option on the site was getting a null reference error because they did not use the login control to obtain the cookie.
What is the best method to get around this? I was thinking if a user was logged in but the AuthenticatedUser class could not get a Connectionstring to log out the user to force them to use the login control again. What should I do?
Try use:
try
{
FormsAuthentication.SignOut();
}
finally
{
Response.Redirect("~/Home.aspx");
}
This way is preferable, for example if in some time you will decide not- cookie auth, but URL based - the FormsAuthentication will manage it gracefully.

How to determine Session Timeout using when reusing CookieContainer

I have the following code which re-uses a CookieContainer which logs in on the first request, but just uses the cookie container for requests after.
After a period of time if idle the site will give a Session Timeout, I will need to perform the login again.
Q: Can I determine (with the cookie container object) if the timeout has happened or is it best to determine if it has happened from the HttpWebResponse which happens to contains text like 'session timeout'. What is the best way to do this?
private static CookieContainer _cookieContainer;
private static CookieContainer CurrentCookieContainer
{
get
{
if (_cookieContainer == null || _cookieContainer.Count == 0)
{
lock (_lock)
{
if (_cookieContainer == null || _cookieContainer.Count == 0)
{
//_cookieContainer.GetCookies(
_cookieContainer = DoLogin();
}
}
}
return _cookieContainer;
}
set
{
_cookieContainer = value;
}
}
And then this method calls out to the container:
public static string SomeMethod(SomeParams p)
{
HttpWebRequest request_thirdPartyEnquiryDetails = (HttpWebRequest)WebRequest.Create(thirdPartyEnquiryDetails);
CookieContainer cookieContainer = CurrentCookieContainer;
request_thirdPartyEnquiryDetails.CookieContainer = cookieContainer;
//... and it goes on to submit a search and return the response
}
Well, since the timeout is 30 mins, I have set the login to repeat after 25 mins.
private static DateTime? lastLoggedIn;
private static CookieContainer _cookieContainer;
private static CookieContainer CurrentCookieContainer
{
get
{
if (_cookieContainer == null || _cookieContainer.Count == 0 || !lastLoggedIn.HasValue || lastLoggedIn.Value.AddMinutes(25) < DateTime.Now)
{
lock (_lock)
{
if (_cookieContainer == null || _cookieContainer.Count == 0 || !lastLoggedIn.HasValue || lastLoggedIn.Value.AddMinutes(25) < DateTime.Now)
{
_cookieContainer = DoLogin();
lastLoggedIn = DateTime.Now;
}
}
}
return _cookieContainer;
}
set
{
_cookieContainer = value;
}
}
As an extra precaution, I check the HttpResponse for the text which returns when the page session times out (although its now expected this won't be seen). If this happens I set the lastLoggedIn date to null and run the search method again.
You can extract all cookies for a domain using the CookieContainer.GetCookies(string uri) method. Using this CookieCollection you can get the cookie you are interested in and check its Expired property to see if it has expired.
There is one thing you should note: Your session may end even if your cookie is valid. IIS may restart the app domain the web application runs in and in that case all authenticated users may loose their session data. So checking the cookie is generally not enough to ensure that you stay logged in.
I am not sure what you want to achieve, but you should notice that CookieContainer has a bug on .Add(Cookie) and .GetCookies(uri) method.
See the details and fix here:
http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain-handling-bug-fix.html

Resources