Put the [Authorize(Roles="admin")] on a view, it works as the user is redirected, however, they are always redirected to the login view despite the fact they are already logged in.
How can I change this so it goes to an error page or something similar?
you can create a custom authorize attribute like this
public class CustomAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Home", action = "UnAuthorized" }));
}
}
}
and use it like this
[CustomAuthorize(Roles="admin")]
Hope this helps
Instead of the duplicate offered I used code from the question: Prevent FormsAuthenticationModule of intercepting ASP.NET Web API responses and modified accordingly:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeCustom : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new RedirectResult("/Error/Unauthorized");
}
else
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}
base.HandleUnauthorizedRequest(filterContext);
}
}
}
Then just created a view for the "/Error/Unauthorized" route and changed the attribute [Authorize] to [AuthorizeCustom]. Now unauthorized people will be redirected to login as expected and people who aren't in roles are redirected to a custom view.
Related
I have a custom auth set up so that a user is stored as a session variable. Once they go through the Account/LogIn process I store the details returned from a 3rd party API as a user in the session like this:
Session["User"] = new UserViewModel(result);
I want to check the user is present before every controller action so I have made a BaseController with the following check in it:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Session["User"] != null)
base.OnActionExecuting(filterContext);
else
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { action = "LogIn", controller = "Account" }));
Each of the controllers then inherits from the BaseController so that it redirects to the Log In page if there is no user. I don't inherit from the BaseController for the AccountController so that it doesn't get in an infinite loop of checking and redirecting, but I also want to have specific pages not check for log in. Is there any way to do this, i.e. write an exception rule in the same way that you might have [AllowAnonymous]?
You could use a filter on those methods as:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class ActionCheckAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower().Trim();
string actionName = filterContext.ActionDescriptor.ActionName.ToLower().Trim();
// this is just a sample.. you can implement any logic you want
if (!actionName.StartsWith("your method name") && !controllerName.StartsWith("your controller name"))
{
var session1 = HttpContext.Current.User.Identity.Name;
HttpContext ctx = HttpContext.Current;
//Redirects user to login screen if session has timed out
if (session1 == null)
{
base.OnActionExecuting(filterContext);
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "Account",
action = "LogOff"
}));
}
}
}
}
then on the controllers put the attribute as:
[ActionCheck]
public class MyController : Controller
{
public ActionResult Index()
{
return View();
}
}
or on specific action methods as:
[ActionCheck]
public Actionresult SomeMethod()
{
return View();
}
For my mvc5 project i have implemented default identity but changed it according to requirements. Now i wanted to redirect unauthorized users to an view which has been created by me. I created a custom authorize filter. When an unauthorized user enters it comes to my error view. I can recognize it by the URL. But the problem is it is not showing the content in the view. Instead it is showing HTTP 404 error. I have put my code below. I know this has been asked here several times. But still i couldn't solve it. All help appreciated. Thanks in advance!
public class CustomAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
}
}
}
ErrorController
public class ErrorController : Controller
{
// GET: Error
public ActionResult Index()
{
return View();
}
public ActionResult AccessDenied()
{
return View();
}
}
AccessDenied view
<h2>AccessDenied</h2>
Access Denied
On a particular controller
[CustomAuthorize(Roles = "Admin")]
public class ProductTypeController : Controller
{
}
Error im getting
HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
UPDATED QUESTION
Now i want to redirect Unauthorized users to ErrorView and Unauthenticated users to Login page. I have put my modified CustomAuthorise below. But it's not working. Pls guide me..
public class CustomAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!httpContext.Request.IsAuthenticated)
return false;
else
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
}
}
For redirecting unauthorized users you don't need to customize AuthorizeAttribute. Simply in Startup.ConfigureAuth(IAppBuilder app) method or your custom OWIN startup method add following line:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Error/AccessDenied"),
});
}
But if you want differentiate between unauthenticated users and unauthorized. Write your custom filter like this:
public class MyAuthAttribute: AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if(filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("/Error/AccessDenied");
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
Then you could add log in url in OWIN startup method:
LoginPath = new PathString("/Account/Login")
I am trying to lock users that are authenticated but not active in their profile page (specific controller/action). By active, I mean that the authorization process needs to check the db to see if the account is active (just a column with boolean data).
This is what I have tried:
However this is getting into a loop.
The idea is:
if authenticated and active = show the controller/action.
if not authenticated = show login page (forms auth)
if authenticated and not active = show profile page.
public class CustomAuthorize : AuthorizeAttribute
{
private ISADietRepository repository;
public CustomAuthorize()
{
this.repository = new SADietRepository(new SADietEntities());
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
//base.OnAuthorization(filterContext);
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
string user = filterContext.HttpContext.User.Identity.Name;
var result = repository.GetVetClinicByEmail(user);
if (!result.IsActive)
{
filterContext.Result =
new RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary{{"Controller", "SADiet"},
{"Action", "NewCustomer"},
});
}
}
else
{
filterContext.Result =
new RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary{{"Controller", "SADiet"},
{"Action", "Login"},
});
}
base.OnAuthorization(filterContext);
}
}
Guys I just tried this code below. It works as long as the user is authenticated. If the user is not authenticated, it no longer asks for authentication, it just run the action with no user logged in.
public class CustomAuthorize : ActionFilterAttribute
{
private ISADietRepository repository;
public CustomAuthorize()
{
this.repository = new SADietRepository(new SADietEntities());
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
string user = filterContext.HttpContext.User.Identity.Name;
var result = repository.GetVetClinicByEmail(user);
if (!result.IsActive)
{
filterContext.Result =
new RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary{{"Controller", "MyAccount"},
{"Action", "Profile"},
});
}
}
base.OnActionExecuting(filterContext);
}
}
I have found the problem.
My second code is correct. The problem is that I replaced the attribute instead of adding the new one to decorate the controller.
I was doing this:
[CustomAuthorize]
public class SADietController : Controller
instead of this:
[CustomAuthorize, Authorize]
public class SADietController : Controller
all sorted!
Pls find the link below that will help to resolve your issue.
ASP.NET MVC 4 custom Authorize attribute - How to redirect unauthorized users to error page?
ASP.NET MVC - How to show unauthorized error on login page?
http://prideparrot.com/blog/archive/2012/6/customizing_authorize_attribute
Custom Authorize Attribute
http://www.tsjensen.com/blog/post/2010/10/16/ASPNET+MVC+Custom+Authorize+Attribute+With+Roles+Parser.aspx
http://mvcsitemap.codeplex.com/workitem/6333
http://code.commongroove.com/2012/04/20/asp-net-mvc-simple-custom-authorization-by-inheriting-from-the-authorizeattribute/
On my MVC4 internet application I am using the AccountController that comes by default along with roles etc.
So I have this controller in which I have defined roles to access the actions, example below.
public class SomeController : Controller
{
private SomeDbContext db = new LookbookDbContext();
//
// GET: /Default1/
[Authorize(Roles = "Administrator")]
public ActionResult Index()
{
return View(db.SomeTable.ToList());
}
...
}
What I wanted now is that, when a user/anonymous tries to access this Index action, get's a custom error view I have made instead of showing the Login form.
I have added this but it just does nothing. I keep getting the login form page. I changed it, for testing porpuses, to give me the default 401 error page but it doesn't work either.
public class CustomAuthorize : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
You should just be able to redirect to your custom error view from your attribute.
Example
public class UnAuthorizedRedirectAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.RequestContext.HttpContext.Response.Redirect("~/error/no-bacon");
}
}
Obviously, the first thing you need to do is make your custom view.
Now, I would reccomend making an action filter to handle this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
}
else
{
filterContext.RequestContext.HttpContext.Response.Redirect("~/shared/error");
}
}
}
NOTE: This answer was added to the question. I'm moving it here to conform to site guidelines.
What I was missing was the [CustomAuthorize] attribute on my Actions. Once I have added that to the desired action it worked.
Asp.net MVC2 does redirect to login page with response 302 when authenticated user has no rights.
I would like to split into two actions
If user is not authenticated then do what it does, redirect to login page.
If user is authenticated but has no required rights then return appropriate http status code and show no rights dude page.
Is there any way to do it? Or am I doing something wrong with authorize and form authentication? Only way I can think of is by writing custom authorize attribute, which I want to avoid.
You could write custom filter attribute like this:
public class CustomAuthorizeAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity == null || !filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult(System.Web.Security.FormsAuthentication.LoginUrl + "?returnUrl=" +
filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.RawUrl));
}
//Check user right here
if (userNotRight)
{
filterContext.HttpContext.Response.StatusCode = 302;
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
And use it in controller:
[CustomAuthorize]
public class HomeController : Controller
{
}
You could write a custom authorize attribute and in the AuthorizeCore method if the user is not authenticated return a HttpUnauthorizedResult and if he is authenticated but not in roles perform some other action you would like. Note that if you return 401 status code the FormsAuthentication framework will eventually redirect with 302 to the login page.
As suggested in Customizing authorization in ASP.NET MVC, you could subclass the AuthorizeAttribute to intercept the authenticated-but-unauthorized scenario and replace the result with a redirect.
Implement a custom AuthorizeAttribute and add the following override. The basics is to check if user is authenticated but not authorized and then redirect to you own "Access Denied" page. Hope this helps!
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
// Check if user is authenticated and if this action requires authorization
if (filterContext.HttpContext.User.Identity.IsAuthenticated
&& filterContext.ActionDescriptor.IsDefined(typeof(AuthorizeAttribute), true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AuthorizeAttribute), true))
{
List<object> attributes = new List<object>(filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true));
attributes.AddRange(filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), true));
// Check all authorzation attributes
foreach (var attribute in attributes)
{
var authAttribute = attribute as AuthorizeAttribute;
if (authAttribute != null)
{
if (!filterContext.HttpContext.User.IsInRole(authAttribute.Roles))
{
// User is not authorized so redirect to our access denied error page
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "area", "" },
{ "controller", "Error" },
{ "action", "AccessDenied" }
});
break;
}
}
}
}
}
Similar to solutions suggested by #hellangle and #Andreas, I used the following code to solve this problem:
public class CustomizedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var userAuthInfo = GetUserAuthInfo();
if (!userAuthInfo.IsAuthenticated())
{
filterContext.Result = new RedirectResult(UrlToYourLoginPage);
return;
}
if (!userAuthInfo.IsAuthorized())
{
var result = new ViewResult {ViewName = "UnAuthorized"};
result.ViewBag.Message = "Sorry! You are not authorized to do this!";
filterContext.Result = result;
}
}
}
Of course, you need to implement the user authorization information class and related methods (GetUserAuthInfo, IsAuthenticated, IsAuthorized) according to your specific needs. Also a View named 'UnAuthorized' should be put to somewhere the MVC engine can find. Then it can be used on a controller class (pointed out in #hellangle's answer) or a action method:
[CustomizedAuthorizeAttribute]
public class TargetController : Controller
{
[CustomizedAuthorizeAttribute]
public ActionResult TargetAction()
{
// Your Code
}
}
In order to provide different access control strategy for various controller classes and action methods, implements a constructor for CustomizedAuthorizeAttribute class which accepts parameter(s) representing access control information and then Instantiate CustomizedAuthorizeAttribute class accordingly.