How do I invalidate a bad authentication cookie early in the request? - asp.net-mvc

Scenario
I develop an MVC application that will be hosted in Windows Azure. During development, I test against a local database and local user membership service. In production, the application hits off a SQL Azure database and a cloud-hosted user membership service.
Sometimes I'll log in to the local version of the site with a user account that only exists on my local machine. If I forget to log out but switch my machine out of test mode, things start to break.
The Problem
I set my hosts file to point my application URL at 127.255.0.0 so that browser requests are handled by the Azure emulator locally. This also means my test application and the production application use the same host name - cookies for one are seen as valid cookies for the other.
If I log in to my local system, an ASPXAUTH cookie is created for the domain and contains the credentials of the user from my local system. If I forget to log out and switch my hosts file back to normal (browser requests go to the live application), it sends the same ASPXAUTH cookie to the server.
Since this user doesn't exist on the server, only locally, any requests like Membership.GetUser().Email return an object null exception.
What I'd Like to Do
Ideally, I could inject some code early in the request to check that the user exists. Something like:
MembershipUser user = Membership.GetUser();
if(user == null) {
FormsAuthentication.SignOut();
}
This will automatically remove the ASPXAUTH cookie for invalid users. But where do I put it? I started looking at my controllers - all of them inherit from a BaseController class, but even putting this code in the base controller's constructor isn't early enough. I also looked at Global.asax, but I'm not sure which event would be best to tie this action to.
Also, I'm not even sure this is the right way to do things. If not, what is the best way to ensure that the authentication cookie is for a valid user before just depending on the membership class like I am currently?

You may handle the below event in you global.aspx:
public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
{
}
EDIT:
In the above method, Membership.GetUser() will return null, since it takes the userName from HttpContext.Current.User.Identity.Name property and in this event the user has not yet been authenticated. The above method is invoked by FormsAuthenticationModule when it raised Authenticate even. You can handle this event like so:
public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
{
if (FormsAuthentication.CookiesSupported &&
Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
var formsAuthTicket = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value);
MembershipUser user = Membership.GetUser(formsAuthTicket.Name);
if (user == null)
{
FormsAuthentication.SignOut();
Request.Cookies[FormsAuthentication.FormsCookieName].Value = null;
}
}
catch (Exception ex)
{
//TODO:log ex
}
}
}
You can also consider to handle the AuthenticateRequest event by declaring the below method in the global.aspx.
NOTE: Application_AuthenticateRequest event is fired after the FormsAuthentication_OnAuthenticate event.
void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated)
{
MembershipUser user = Membership.GetUser();
if (user == null)
{
FormsAuthentication.SignOut();
HttpContext.Current.User = null;
}
}
}
Hope this helps..

You create a base controller that overrides the OnActionExecuting and place your code there.
Have all your controller controllers inherit from him. This the OnActionExecuting from a older project I had
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Request.IsAuthenticated)
{
if (Session[SomeKey] == null)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "controller", "Account" }, { "action", "LogOff" } });
}
}
base.OnActionExecuting(filterContext);
}
I'm basically checking if the request is authenticated otherwise I redirect it to logout action.
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onactionexecuting.aspx

Related

Fire event after user is authenticated - windows authentication

Is there an event that fires after user is authenticated (Windows)? I am executing at Application_Start(), but it returns false User.Identity.IsAuthenticated.
What I would like is to create and add roles manually to a user.
I used this:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is WindowsIdentity)
{
if (string.IsNullOrEmpty(Usuario.Nome))
{
Make sure, to in you project properties (alt + enter in the project) click on WEB an check NTLM AUTHENTICATION.
This worked for me. now HttpContext.Current.User.Identity is no longer null
Application_Start() is called when the first resource (such as a page) in an ASP.NET application is requested. Also, it’s called only once during the life cycle of an application. That has been said, you will not will be able to auth all the users at that point. The current Windows user information on the client computer is supplied by the web browser through a cryptographic exchange involving hashing with the Web server [Wiki]. It is only then that you can auth the user. So, User.Identity.IsAuthenticated should work on page load (see code below). You can extract the logic that you need to a common method and call it on the first time a page loads
protected void Page_Load(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
Page.Title = "Home page for " + User.Identity.Name;
// add your logic here
}
else
{
// you get here if auth failed.
Page.Title = "Home page for guest user.";
}
}
More info after your update:
I would use Application_Start() to check and add roles if they do not already exist.
if (! Roles.RoleExists("Auditors"))
Roles.CreateRole("Auditors");
You might find this post useful: http://weblogs.asp.net/scottgu/Recipe_3A00_-Implementing-Role_2D00_Based-Security-with-ASP.NET-2.0-using-Windows-Authentication-and-SQL-Server

Hybrid of Windows Authentication and Forms Authentication in ASP.NET MVC 4

We have an ASP.NET MVC 4 intranet application. We’re using Windows Authentication and that aspect works fine. The user’s credentials are used and we can access those credentials from the web app.
What we really want is some sort of hybrid mode, however. We want to get the user’s credentials from the browser, but we also want to verify that the user is in our application’s database. If the user’s in the database, then they can just continue on. If they’re not, we want to redirect them to a page asking for alternate credentials. What I’m doing now is, in Global.asax.cs, I’ve got an Application_AuthenticateRequest method and I’m checking to see if the user is authenticated. If they are and their cookie information doesn’t reflect the fact that they’re logged into the system, then I log them in and set up some cookies with info about the user. If they’re not authenticated, I redirect them to a login page. We can’t use AD roles for reasons involved with company policy, so we need to use the database for additional authentication.
I’m guessing Application_AuthenticateRequest isn’t the place to do this, but maybe it is. But we basically need a place to filter the requests for authentication. But additionally this implementation leads me to another issue:
We have certain URLs in our app that allow anonymous access. I’ve added <location> tags to the web.config for these. The problem is, when anonymous calls are made into these, it gets to Application_AuthenticateRequest and tries to log the user into the DB. Now, I can add code into Application_AuthenticateRequest to handle these URLs and that’s currently my plan, but if I’m write and Application_AuthenticateRequest isn’t the place to be doing this, then I’d rather figure it out now than later.
You need to use Action Filters for this purpose. You can extend the AuthorizeAttribute like this:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private UnitOfWork _unitOfWork = new UnitOfWork();
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = false;
var username = httpContext.User.Identity.Name;
// Some code to find the user in the database...
var user = _unitOfWork.UserRepository.Find(username);
if(user != null)
{
isAuthorized = true;
}
return isAuthorized;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (AuthorizeCore(filterContext.HttpContext))
{
SetCachePolicy(filterContext);
}
else
{
// If not authorized, redirect to the Login action
// of the Account controller...
filterContext.Result = new RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary {
{"controller", "Account"}, {"action", "Login"}
}
);
}
}
protected void SetCachePolicy(AuthorizationContext filterContext)
{
// ** IMPORTANT **
// Since we're performing authorization at the action level,
// the authorization code runs after the output caching module.
// In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later
// be served the cached page. We work around this by telling proxies not to
// cache the sensitive page, then we hook our custom authorization code into
// the caching mechanism so that we have the final say on whether a page
// should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidationHandler, null /* data */);
}
public void CacheValidationHandler(HttpContext context,
object data,
ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}
Then, you can use this attribute at the Controller level or Action level like this:
[MyAuthorize]
public ActionResult SomeAction()
{
// Code that is supposed to be accessed by authorized users only
}

ASP.NET MVC 4 remember me behavior

I've got the remember me option available on the login page. Could somebody please explain the process? i.e. is going through the logging in process every time user navigates to the page? or is the user constantly logged in and the application only checks the credentials when the user logs off and in again? The reason I ask is that I have IsEnabled property on the user table in DB and would like to disable users. But this property doesn't seem to make any difference unless user logs off and in again.
Any Ideas?
Thanks.
When the user logs in for the first time with valid credentials, a cookie is created and stored client side. With each subsequent request to the website the cookie is passed to the server and validated to ensure it has not expired and is valid. If you want to be able to check if your IsEnabled should allow a user to access the site, you need to create your own authenticatin logic in the Application_AuthenticateRequest event in your Global.asax file.
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
// Your authentication logic
}
You can view a full list of events you can hook into here: http://www.dotnetcurry.com/ShowArticle.aspx?ID=126
I have used this variant
Add this attribute to your controller
public class IsLocked : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!httpContext.Request.IsAuthenticated)
return false;
var session = DependencyResolver.Current.GetService<ISession>();
var userDb = session.Query<Admin>().SingleOrDefault(x => x.Email == httpContext.User.Identity.Name);
if (userDb == null)
return false;
return userDb.Status == null || userDb.Status.Value == false;
}
}

ASP.NET MVC request context details

Where can I plug in a custom provider to set up the request context?
I want to run an ASP.NET MVC application in "slave" mode while gradually transitioning features from a legacy system. Each request will have a cookie, and I want to grab the cookie, make an external call to resolve it to a user identity, and set up that user identity for the remainder of the request.
I might set a forms authentication cookie, or use Session, but the source of truth about authentication has to be the externally-set cookie, on every request.
What's the best way to do this? Where do I plug in? I've looked at Authentication providers, and the Authorization attribute, but neither of those seems the right place for this.
I would have thought an HttpModule would be ideal for this scenario?
If I understand you correctly, I did something similar on a project I was working on recently:
public class UserSessionHttpModule : IHttpModule
{
private HttpApplication mApplication;
public void Dispose()
{
}
public void Init(HttpApplication context)
{
mApplication = context;
context.BeginRequest += new EventHandler(CheckUserSession);
}
private void CheckUserSession(Object sender, EventArgs e)
{
var extension = Path.GetExtension(mApplication.Context.Request.Path);
if (extension == "" || extension == ".aspx")
{
var userSessionService = ObjectFactory.GetInstance<IUserSessionService>();
userSessionService.CheckUserSession();
}
}
}
you could override the Init method in Global.asax page and listen to the PostAuthenticateRequest event, the events fires after authentication so you could change whatever values you like from the form authenticate cookies and inject your own

Should I Use Session.Abandon() in my LogOff Method?

Technologies I'm Using:
MVC v2
Forms Authentication (Sliding Expiration)
Session State Server
Custom Authorization Attribute
I'm using the state server process for my mvc app. During testing, when an authenticated user would click the "LogOff" button, it would correctly take them to the authentication screen, and upon successful credential entering, would log them back in. BUT, it would find their prior session variable state, and NOT reload any new permissions I'd given them. This is due to how I'm loading a user in the following code:
public override void OnAuthorization(AuthorizationContext filterContext) {
if (filterContext == null)
throw new ArgumentNullException("FilterContext");
if (AuthorizeCore(filterContext.HttpContext)) {
IUser customUser = filterContext.HttpContext.Session["CustomUser"] as IUser;
if ((customUser == null) || (customUser.Name != filterContext.HttpContext.User.Identity.Name)) {
customUser = new User(filterContext.HttpContext.User.Identity.Name,
filterContext.HttpContext.User.Identity.IsAuthenticated);
}
if (_privileges.Length > 0) {
if (!customUser.HasAtLeastOnePrivilege(_privileges))
filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
}
filterContext.HttpContext.Session["CustomUser"] = customUser;
}
}
So, you can see I'm storing my customUser in the Session and that value is what was fetched from the prior session even though the user had logged off between (but logged back on within the sliding expiration window.
So, my question is, should I place a simple Session.Abandon() call in my LogOff method in the AccountController, or is there a cleaner more advantageous way of handling this?
Normally Session.Clear() should be enough and remove all values that have been stored in the session. Session.Abandon() ends the current session. It might also fire Session_End and the next request will fire Session_Start.

Resources