User's last login with Forms Authentication - asp.net-mvc

I need to record the last login info of a user into the database. So I'm thinking to put something like the code below in all of my controllers' action methods.
if (User.Identity.IsAuthenticated == true)
{
// save DateTime.Now for this user
}
However, I have a moderately huge number of controller actions. And this approach will also be difficult to manage in the long run. Is there a simpler and best way to do this?
Thanks.
[edit]
With Forms Authentication, when the logon form's "remember me" is checked, the next time the user comes back they don't go to the login page anymore. That's the reason I came up with the idea above. But I really don't like it and I will appreciate your suggestions.

The built-in MembershipUser type (which you are likely using) has a LastLoginDate and a LastActivityDate property. You should use those if possible.
If not, you can handle both "last login" and "last access" with an HttpModule:
public class LastTimeModule : IHttpModule
{
public void Init(HttpApplication context)
{
// This event is raised prior to *any* handler request ("last access")
context.PreRequestHandlerExecute += new EventHandler(OnPreRequest);
// This event is raised when a user is authenticated ("last login")
context.PostAuthenticateRequest += new EventHandler(OnPostAuthenticateRequest);
}
void OnPostAuthenticateRequest(object sender, EventArgs e)
{
// Log time here
}
public void OnPreRequest(Object source, EventArgs e)
{
// Log time here
}
}

If you're using MVC you could create a custom action filter and apply it to your controllers.

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

MVC3: Session_Start Fires twice when testing for Roles

I need to do some authentication for a web app with MVC3. The customer would like there to be a generic page to show if they do not have any of the role groups in windows AD that are allowed to use the app. I found a pretty simple way to do it, but just curious if it is a valid way or if there is something better out there.
Basically in the Session_Start in the global I am checking for User.IsInRole() and if that returns false then I do a Response.Redirect(). This question is: after it his the code in the IF statement and hits the Response.Redirect() code then it hits the session one more time before it goes to the AccessDenied page in the root of the app. Is this okay? Will it cause any issues If they are valid and does not enter the If to do the response.redirect?
//if (!User.IsInRole("test_user"))
//{
// Response.Redirect("~/AccessDenied.aspx", true);
//}
I would recommend you to write your Authorization filter for MVC3 and do this type of logic there:
public class RoleFilter: AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext filterContext)
{
if (!User.IsInRole("test_user"))
{
filterContext.HttpContext.Response.StatusCode = 302;
filterContext.Result = new RedirectResult("~/AcessDenied.aspx");
}
}
}
Also I wouldn't recommend you to use Response.Redirect because it aborts current thread.

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

How do I invalidate a bad authentication cookie early in the request?

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

Resources