Fire event after user is authenticated - windows authentication - asp.net-mvc

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

Related

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
}

How to Make Sure My Custom ClaimsPrincipal Sticks Around In ASP.NET MVC 4

I am creating a custom ClaimsPrincipal that has additional claims using a custom AuthenticationManager. It appears that in ASP.NET 4.5 the AuthenticationManager is not automatically invoked and you have to manually invoke it. I have seen posts that suggest you do this in the PostAuthenticateRequest. My code looks like this in the Global.asax:
protected void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
System.Web.HttpContext.Current.User =
FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager.Authenticate("none",
System.Web.HttpContext.Current.User as ClaimsPrincipal);
}
I am using Forms Authentication with cookies. The current principal gets set correctly when the PostAuthenticateRequest is fired. The problem is that when the next request comes in to the ASP.NET Web API the custom claims are lost. I am using Web API for AJAX requests in a single page application (SPA). I suspect that what is in the current ClaimsPrincipal is being overwritten by what is in the cookie, but I am not sure. I am not using an inherited ClaimsPrincipal for type-safe retrieval of custom claims. I am just adding new claims which are subsequently lost somewhere in the process. What is the proper way to setup a custom ClaimsPrincipal so the additional claims are not loss between requests.
Here is the solution I came up with. First I used the FormsAuthentication_OnAuthenticate event instead of the Application_OnPostAuthenticateRequest event. I get the cookie to retrieve the authenticated users name and build a new claims principal with the claims I want to add. I do not add the roles as claims because the system adds them later in the authentication process and ends up duplicating them. This allows me to add custom claims to the principal for future processing in the application.
public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
{
if (FormsAuthentication.CookiesSupported)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
SaClaimsSecurity security = new SaClaimsSecurity();
//Do not add roles here because the security framework adds them
//somewhere else in the chain and will duplicate roles
ClaimsPrincipal principal = security.CreateClaimsPrincipalWithoutRoles(ticket.Name);
args.User = principal;
}
catch (Exception e)
{
// Decrypt method failed.
}
}
}
else
{
throw new HttpException("Cookieless Forms Authentication is not " +
"supported for this application.");
}
}

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

User's last login with Forms Authentication

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.

ASP.NET MVC redirect dilemma on Application_AuthenticateRequest

I would like my MVC application to check (on every request) if the current user has a profile. If no profile is found, I want to redirect them to the "profile" page for them to submit one.
protected void Application_AuthenticateRequest()
{
if (HttpContext.Current.User != null)
{
// Redirect to profile page if the current user does not have a profile
if (!HttpContext.Current.User.HasProfile())
Response.RedirectToRoute("Profile");
}
}
I have extended the IPrincipal to include a method "User.HasProfile()" to check if the user has a profile. It works, but the problem is the that Application_AuthenticateRequest gets called for every single request, including javascript, css etc...
Moreover, it creates a redirect loop when I try to do Response.RedirectToRoute("Profile").
The only way I have found around this is to add the following to my IF statement before redirecting to the profile page:
!HttpContext.Current.User.HasProfile() && Request.Path != "/system/profile" && !Request.Path.Contains(".")
It checks if the current path is not the profile page (to prevent the redirect loop) and if there is a period in the URL (to allow javascript and css resources to continue to load). Is there a better way? How do you guys handle this?
I'm not sure if this will work for every single case, but I found that you can interrogate the Handler property of the current context, and it's null for static resources.
void LogRequestInfo(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpContext ctx = app.Context;
// We don't want to spend resources logging static file requests. This code was added when we moved
// to Integrated mode in IIS.
if (ctx.Handler == null)
return;
// More code below here...
}

Resources