presenting two different login screens based on roles - asp.net-mvc

I was trying to figure out whether it would be possible to present two different login screens based on the authorization role. The requirement is simple. I have two roles say "admin" and "public". There are "Authorize" attributes marked all over my applications action methods for these two roles.
Now the requirements of my application specifies different login screens for "admin" and "public". The "admin" login screen is secured by an additional security code which is not required for "public" login screen. What I was looking for is some way to know who is trying to log in based on the Action method invoked. If the action method invoked is decorated by Authorize[Roles="admin"] then I would present the admin login screen, whereas if action method invoked is applied Authorize[Roles="public"] then I need to present the public login screen.
If the Login Screen is directly invoked then by default the public login screen would be presented.
It may sound a little weird but this is the scenario I am trying to figure out the solution for.

You could write a custom authorize attribute which will redirect to the proper logon action:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
var roles = Roles.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (roles.Contains("admin", StringComparer.OrdinalIgnoreCase))
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "account",
action = "adminlogon",
returnUrl = filterContext.HttpContext.Request.RawUrl
}));
}
else
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "account",
action = "logon",
returnUrl = filterContext.HttpContext.Request.RawUrl
}));
}
}
}
and then decorate your controllers/actions with it:
[MyAuthorize(Roles = "admin")]
public ActionResult Foo()
{
return View();
}
[MyAuthorize(Roles = "normaluser")]
public ActionResult Bar()
{
return View();
}
Now if a non authenticated user tries to hit the Foo action he will be redirected to the /account/adminlogon action and if he tries to hit the Bar action he would be redirected to the /account/logon action. In both cases the current url will be passed as returnUrl parameter so that upon successful login the user could be brought to the page that he was initially trying to browse.

Related

How to exclude some action methods from returnUrl

I'm using returnUrl to return to the last requested page when logging in after being logged out due to inactivity. In itself this works fine, unless the user does not requests a page, but some other action. What I would like is that in case the first action after SessionTimeOut is for example "addItem", the returnUrl is set as "controller/index" instead of "controller/addItem".
After SessionTimeOut the application does not automatically redirect to the login page. The redirect happens as soon as the user makes a new request after SessionTimeOut (as requested by client). So when the user clicks a menu item, the returnUrl is set to the requested page. Great so far.
The issue is though when the user click for example the addItem button that the returnUrl is set as the action method linked to that button. I don't understand how I can exclude this kind of methods from returnUrl.
This is a very scaled down example of the application, to give an idea of how it hangs together.
MainController:
public ActionResult MainPage(){
return View();
}
public ActionResult addItem(){
--- Do Something ---
}
public ActionResult SecondPage(){
return View();
}
Login method:
public ActionResult Login([Binde(Include = "UserName, Password")]LoginModel model, string returnUrl)
{
if(Modelstate.IsValid)
{
--- Do Validation ---
return Redirect(returnUrl);
}
}
In case the user is on the MainPage and makes after SessionTimeOut the request for "SecondPage", the returnUrl should be and is "controller/SecondPage".(OK)
In case the suer is on the MainPage and makes after SessionTimeOut the request for "addItem", returnUrl should be "controller/MainPage" and not "controller/addItem"
One quick solution is to put Action Filters into effect.
As you might know, an action filter is an attribute that you can apply to a controller action -- or an entire controller -- that modifies the way in which the action is executed. More details, see documentation here
In this case, you need to override OnActionExecuting method, is called before a controller action is executed, so that prior to execution, you can decide to redirect or not based on the return value.
I provide you with a simple example :
public class myCheckUrlActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
List<string> avoidList = new List<string> { "addItem", "removeItem"};
foreach(string i in avoidList)
{
if(filterContext.HttpContext.Request.Url.Query.Contains(i))
{
filterContext.Result = new RedirectResult("/Home/MainPage") ;
break;
}
}
}
}
Now, Let's apply this filter to all action methods inside your Home Controller, though you may apply it individually to only certain action methods.
[myCheckUrlActionFilter]
public class HomeController : Controller
{ .... }
Access to HttpContext inside the body of the filter, enables to extract session, and even current controller and action name. Therefore, you can check session state here in addition returnUrl existence to further decide on redirect. So, customize it as you need.
Hope this helps.

MVC Reroute to action when user isn't authorized

I'm currently working on setting up permissions for my web app. Not everyone needs to have access to certain pages, such as creating/editing/deleting (the usually) or adding new rights to users. I have a table in my database that keeps track of the users and their role/rights. I am overriding the AuthorizeAttribute. What I would like to happen is when the user is not authorized to access a page is for them to be redirected back to the page they were just at and a alert show saying they don't have access.
Example: If they are on the Home Page and click the Add New Something Button, if they don't have rights they will be directed back to the Home Page with the error.
To make this work I need to get access to the previous action/controller names since the previous page may never be the same.
Current custom AuthorizeAttribute HandleUnauthorizedRequest Method
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary {
{ "action", filterContext.RouteData.Values["action"] },
{ "controller", filterContext.RouteData.Values["controller"] }
});
}
This gets me the action/controller they are trying to access, am I able to get where they are coming from?
Using Klings and Stephen.vakil advice I have:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
Uri requestUrl = filterContext.HttpContext.Request.UrlReferrer;
if (requestUrl != null)
{
filterContext.Result = new RedirectResult(requestUrl.ToString());
}
else
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary {
{ "action", "NotAuthorized" },
{ "controller", "Admin" }
});
}
}
If there was a referrer then take them back to that page, but I still need a popup to appear when they reach this page. I know that it can be done in each individual view with javascript and the alert(), but I am looking for something that can be done in one place without adjusting the other views if possible. If not, send to a generic page stating they aren't authorized.

Restricting access to action methods in controller in Asp.net MVC

I am new to Asp.net MVC web development and I developed one application using it. In my application I am using my own authentication and authorization check as follows:
I create Login controller in that created Login action method like this
[HttpPost]
public ActionResult Login(LoginViewModel Info)
{
if (ModelState.IsValid)
{
if (checking username and password exist in DB or not)
{
//Adding required values in session
Session["username"] = Info.Username;
//Redirect to dashboard
}
else
{
//not found redirect to login page
}
}
return View();
}
Now when accessing action methods in Admin controller I used my "custom authorize" attribute for checking user is logged-in or not and have rights for method
public class AdminController : Controller
{
[CustomAuthorize(ValidRole = "Admin")]
public ActionResult Index()
{
return View();
}
}
For this I override default AuthorizeAttribute like this
public class CustomAuthorize : AuthorizeAttribute
{
// Custom property
public string ValidRole { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Session["username"] == null)
{
//User is not logged-in so redirect to login page
return false;
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Login",
action = "Login"
})
);
}
}
This code works fine for me. My question that is there any better solution for checking whether user is logged-in or not and according to it redirect user to login or dashboard page so that user can't manipulate url and get access to functionality to which he is not authorized.
thanks in advance
My question that is there any better solution for checking whether
user is logged-in or not and according to it redirect user to login or
dashboard page so that user can't manipulate url and get access to
functionality to which he is not authorized.
Yes, there's already a built-in method for doing this that does not rely on ASP.NET Sessions. It is called Forms Authentication.
You don't need to be writing any custom Authorize attributes. Once you verified the credentials of the user simply set the FormsAuthentication cookie:
if (checking username and password exist in DB or not)
{
// Emitting forms authentication cookie
FormsAuthentication.SetAuthCookie(Info.Username, false);
//Redirect to dashboard
}
and then simply use the built-in Authorize attribute to decorate your protected controller actions:
public class AdminController : Controller
{
[Authorize(ValidRole = "Admin")]
public ActionResult Index()
{
// At this stage the user is authenticated and has the role Admin.
// You could get the current username using the User.Identity.Name property
return View();
}
}
Forms Authentication is stateless. It does not rely on any state on the server to track the currently authenticated user on the server. The information about the current user is contained in an encrypted forms authentication cookie that is sent along each request. This way you don't need to be thinking about handling complex scenarios when your application is hosted in a web farm in which case you would have needed to use distributed ASP.NET Sessions.

Share Action with Authorized and Unauthorized User in ASP.NET MVC Controller

I have an ASP.NET MVC app that with a controller. All of the actions in this controller can be accessed by anonymous users. However, if the user is authenticated, I want to do something special in the action. Currently, I've noticed that no matter what, User.Identity.IsAuthenticated is always false in the context of this action. Here is my code:
public class MyController : Controller
{
public ActionResult GetProfile(string id)
{
if (User.Identity.IsAuthenticated) {
ViewBag.ShowAuthStuff = true;
} else {
ViewBag.ShowAuthStuff = false;
}
}
}
How do I make it such that both an authenticated and an unauthenticated user can access the same action, but do different things? I can't figure out why User.Identify.IsAuthenticated is always false. I checked my cookies. When I'm logged in, there is a cookie named:
.ASPXAUTH
However, when I visit the action, that cookie is no longer available.
Just use both Authorize and AllowAnonymous filters:
[Authorize]
[AllowAnonymous]
public ActionResult GetProfile(string id)
{
if (User.Identity.IsAuthenticated) {
ViewBag.ShowAuthStuff = true;
} else {
ViewBag.ShowAuthStuff = false;
}
}
Though it doesn't make a whole lot of sense to have anonymous access to a "profile".
Also, typically, you don't want to mix authorized and unauthorized actions in the same controller. It's better to have actions that must or may require authorization in a controller together, and unauthorized actions in a separate controller. In that case, you specify the Authorize filter on the controller itself, and then AllowAnonymous on any individual actions that want to interact with authenticated users, but don't require it.
For example in an "Accounts" controller:
[Authorize]
public class AccountsController : Controller
{
public ActionResult Profile()
{
// Login required to reach here
}
[AllowAnonymous]
public ActionResult Login()
{
if (User.Identity.IsAuthenticated)
{
// Already logged in, redirect to profile
return RedirectToAction("Profile");
}
// Show login form for anonymous user
return View()
}
}

ASP.NET MVC - Navigation Approach

I am new to ASP.MVC. My background is in ASP.NET Web Forms, I think this is what is causing my confusion. I understand that the "M" basically represents the data source, the "V" represents the resource I'm requesting and the "C" dictates what gets shown to an end-user. But then I get confused.
For instance, I'm just trying to create a Login screen. I envision a user visiting "http://www.myapp.com/Account/Login" and they will be presented with a traditional Login screen. To accomplish this, I have added the following in the RegisterRoutes method in my Global.asax file:
routes.MapRoute(
"Login",
"{controller}/{action}",
new { controller = "Account", action = "Login", id = "" }
);
The Login action executes, but this is where I get confused. You see, the first time the login screen loads, I would expect to just show a username/password field. Then on post, I would expect the form to be validated and processed. In an attempt to do this, I have created the following method:
public ActionResult Login()
{
bool isFormValid = ValidateForm();
if (isFormValid)
LoginUser();
else
ShowErrors();
return View();
}
My confusion rests with the Login action. Initially, there is no data. But the next time, I want to validate the data. How do I determine if the Action is a postback or not?
Thank you!
The easiest way to handle this is with two actions: one for get, one for post. Use the AcceptVerbs attribute to control which gets invoked depending on the method. BTW, the default routes should work just fine for this case since when the controller and action is supplied it gets directed as you would expect. I thought that this scenario was also covered in the project template -- did you set up a project using the template or an empty one?
[AcceptVerbs( HttpVerbs.Get )]
public ActionResult Login()
{
}
[AcceptVerbs( HttpVerbs.Post )]
public ActionResult Login( string userName, string password )
{
}
You need two different methods, for Post and Get.
[AcceptVerbs (HttpVerbs.Get]
public ActionResult Login ()
{
return View ();
}
[AcceptVerbs (HttpVerbs.Post]
public ActionResult Login (FormCollection form)
{
if (AuthenticationSuccess ())
return RedirectToAction ("Account");
else
return View ();
}
For Post version you can use the model binding mechanism:
public ActionResult Login (LoginModel loginModel)
Or
public ActionResult Login (string LoginName, string Password)

Resources