How to enable Authentication on whole controller and disable only for certain action methods. I want authentication for all resources. If I write something like that:
[Authorize]
public class HomeController : BaseController
{
//This is public
[UnAuthorized]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
//This is private resource
public ActionResult PrivateResource()
{
return View();
}
}
Then anyone can access this resource. I need this because we have all resources are private and very few are public on our project. Do you have any ideas how to make it better way?
Organize your controllers accordingly. Have a base controller for all authenticated resources which you could annotate with the [Authorize] attribute and another one for public resources.
[Authorize]
public abstract BaseAuthenticatedController : Controller
{ }
public abstract BaseController : Controller
{ }
Based on solution which is found here I wrote the code that fixes exactly what I wanted.
Create custom authorization attribute base on AuthorizeAttribute and override method OnAuthorization:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext != null)
{
object[] attributes = filterContext.ActionDescriptor.GetCustomAttributes(false);
if (attributes != null)
{
foreach (var attribute in attributes)
if (attribute is UnAuthorizedAttribute)
return;
}
}
base.OnAuthorization(filterContext);
}
I'm using a reflection here to recognize an action with UnAuthorized attribute. I don't know about performance issues in this case, but it solves the problem completely.
It's really strange that no one said about AllowAnonymous attribute which services for such situations:
[Authorize]
public class HomeController : BaseController
{
//This is public
[AllowAnonymous]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
//This is private resource
public ActionResult PrivateResource()
{
return View();
}
}
Related
I have a couple of controllers like this:
[RoutePrefix("side-navigation")]
public class SideNavigationController : BaseController
{
[Route("{pathname}")]
public ActionResult Index(string pathname)
{
SideNavigationPopoutModel model = _sideNavFactory.Value.CreatePopout(pathname);
if (model != null)
{
return View(model);
}
return HttpNotFound();
}
}
public class CatchAllController : BaseController
{
public ActionResult Index(string pathname)
{
CatchAllModel model = _catchAllModelFactory.Value.Create(pathname);
if (model != null)
{
// TODO: Do we need this - what does it do?
// TempData.Restore(this);
return View(model);
}
return HttpNotFound();
}
}
But I cannot seem to get to my index action in the side navigation controller - if I browse to localhost/side-navigation/test it's hitting the catch all controller with side-navigation/test as it's pathname instead of the side navigation one with test as the pathname.
Can anyone see anything I am doing wrong here or how to make the side navigation controller work?
This is the route config:
// MVC attribute routing
routes.MapMvcAttributeRoutes();
// Default catch all route
routes.MapRoute(
"Default",
"{*pathname}",
new { controller = "CatchAll", action = "Index" });
Weirdly, if I change the route of the side navigation index to test/{pathname} and browse to side-navigation/test/test it will work and the controller will be hit but I don't want to add anything before the pathname
It seems that you are not using [Area] also put attribute [Route("[action]")] above method.
Ok I have fixed this by adding an asterisk before the pathname:
[RoutePrefix("side-navigation")]
public class SideNavigationController : BaseController
{
[Route("{*pathname}")]
public ActionResult Index(string pathname)
{
}
}
If anyone can explain why this works and without an asterisk doesn't, it would be greatly appreciated, as I also have a product controller set up in exactly the same way that doesn't need the asterisk
[RoutePrefix("side-navigation")]
public class SideNavigationController : BaseController
{
[Route("{*pathname}")]
public ActionResult Index(string pathname)
{
}
}
So my project requirements changed and now I think I need to build my own action filter.
So, this is my current login controller:
public class LoginController : Controller
{
// GET: Login
public ActionResult Index()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
string userName = AuthenticateUser(model.UserName, model.Password);
if (!(String.IsNullOrEmpty(userName)))
{
Session["UserName"] = userName;
return View("~/Views/Home/Default.cshtml");
}
else
{
ModelState.AddModelError("", "Invalid Login");
return View("~/Views/Home/Login.cshtml");
}
}
public string AuthenticateUser(string username, string password)
{
if(password.Equals("123")
return "Super"
else
return null;
}
public ActionResult LogOff()
{
Session["UserName"] = null;
//AuthenticationManager.SignOut();
return View("~/Views/Home/Login.cshtml");
}
}
And this is my action filter attempt:
public class AuthorizationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (HttpContext.Current.Session["UserName"] != null)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary{{ "controller", "MainPage" },
{ "action", "Default" }
});
}
base.OnActionExecuting(filterContext);
}
}
I have already added it to FilterConfig, but when I login it does not load Default.cshtml it just keeps looping the action filter. The action result for it looks like this:
//this is located in the MainPage controller
[AuthorizationFilter]
public ActionResult Default()
{
return View("~/Views/Home/Default.cshtml");
}
So, what would I need to add in order to give authorization so only authenticated users can view the applicationĀ“s pages? Should I use Session variables or is there another/better way of doing this using? I am pretty much stuck with AuthenticateUser(), since what happens there now is just a simple comparison like the one we have there now.
Thank you for your time.
Create an AuthorizeAttribute with your logic in there:
public class AuthorizationFilter : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
{
// Don't check for authorization as AllowAnonymous filter is applied to the action or controller
return;
}
// Check for authorization
if (HttpContext.Current.Session["UserName"] == null)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
As long as you have the Login URL Configured in your Startup.Auth.cs file, it will handle the redirection to the login page for you. If you create a new MVC project it configures this for you:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(
new CookieAuthenticationOptions {
// YOUR LOGIN PATH
LoginPath = new PathString("/Account/Login")
}
);
}
}
Using this you can decorate your controllers with [AuthorizationFilter] and also [AllowAnonymous] attributes if you want to prevent the authorization from being checked for certain Controllers or Actions.
You might want to check this in different scenarios to ensure it provides tight enough security. ASP.NET MVC provides mechanisms that you can use out of the box for protecting your applications, I'd recommend using those if possible in any situation. I remember someone saying to me, if you're trying to do authentication/security for yourself, you're probably doing it wrong.
Since your attribute is added to the FilterConfig, it will apply to ALL actions. So when you navigate to your MainPage/Default action it will be applying the filter and redirecting you to your MainPage/Default action (and so on...).
You will either need to:
remove it from the FilterConfig and apply it to the appropriate actions / controllers
or add an extra check in the filter so that it doesn't redirect on certain routes
I have an ASP MVC 5 web site, i have several controllers and views, how can i redirect the user aromatically to the Log-In view if he is not authenticated without preceding all the actions by if(User.Identity.Is Authenticated)
Decorate your controllers (if you want all actions within that controller to require authentication) or specific actions with the [Authorize] attribute. You can specify the login url the user gets redirected to in your web.config file.
[Authorize]
public class UserController : Controller
{
public ActionResult Index()
{
// Must be authorized
}
public ActionResult Users()
{
// Must be authorized
}
}
public class ProductController : Controller
{
public ActionResult Index()
{
// Doesn't require authorization
}
[Authorize]
public ActionResult Products()
{
// Must be authorized
}
}
Further reading:
http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute(v=vs.118).aspx
For this task you can make use of the [Authorize] attribute, which you can implement at either the Class or Action level:
[Authorize]
public class AccountController : Controller
{
public AccountController () { . . . }
public ActionResult Register() { . . . }
public ActionResult Manage() { . . . }
public ActionResult LogOff() { . . . }
. . .
}
In the above instance attempting to access any action method in the controller will redirect the user to the login page, after which a further redirection to the initially requested action will be made.
For further information on how to use the Autohorize attribute please see the MSDN article, as it has expanded examples and background information.
You can use the events in the global.asax to authenticate and authorize on every request.
Application_AuthenticateRequest: Fired when the security module has established the current user's identity as valid. At this point, the user's credentials have been validated.
Application_AuthorizeRequest: Fired when the security module has verified that a user can access resources.
So, something like this (this isn't complete though)
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
// you can re-direct them for example
}
}
Answer 1 :
You can use inbuilt [Authorize] attribute in MVC..
[Authorize]
public class YourController : Controller
{
public ActionResult YourAction()
{
}
}
the [Authorize] attribute will check whether user is authenticated or not if not then it will redirect user to login page automatically
Answer 2 :
Or you can make your custom filter attribute as :
[AuthoriseUser]
public class YourController : Controller
{
public ActionResult YourAction()
{
}
}
public class AuthoriseUserAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check user is authenticated
// if user is not authenticated then do as :
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Login", action = "Index" }));
}
}
Answer 3 :
As you have said in your comments section that you don't want to write [Authorize] attribute on every controller in your project then below answer will help you :
public class YourController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//....Check user authentication here
}
}
public class MyController : YourController
{
public ActionResult Myaction()
{
// ...
}
}
In above answer all you have to do is to make your custom base controller YourController and then you can put your authentication stuff there and then every controller in your web app will inherit YourController instead of inbuilt base Controller class.
I have implemented my own custom Authorize attribute.
The attribute is applied both at the controller level and at the action level.
Here is an example of what I need to do:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
public ActionResult SomeOtherAction()
{
return View();
}
}
Currently if a user has the Administrator Role but not the AdvancedUsers role, he cannot execute "Administrative Task".
How can I change this behavior to perform a security check at the action level even if the user is not authorized at the controller level?
For the moment, the only solution I can think about is to implement 2 attributes: one for securing controllers, another for securing actions. Then I would play with the Order property to execute the one at the action level first.
However, I would prefer a solution with a single attribute if possible.
Use built-in [OverrideAuthorization]:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[OverrideAuthorization]
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
public ActionResult SomeOtherAction()
{
return View();
}
}
OverrideAuthorization Attribute is available for MVC 5 (at least) and up. Once you decorate the Action with it, also decorate with the new Role and that will take effect over the Controller level Role.
This should not be possible. Imagine the logic which MVC uses with the authorization filters.
When the controller is determined - check if there is an authorization filter that applies to that controller and execute it.
When the action is known - do the same for the action.
In all cases a fail in authorization would short-circuit the pipeline.
To make specific actions restricted you simply use the Authorize-attribute on the methods that handle these actions.
When you mark an action method with the Authorize attribute, access to that action method is restricted to users who are both authenticated and authorized.
//[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
{
[ClaimsAuthorize(Roles ="Administrators", "Role2","Role3")]
public ActionResult AdministrativeTask()
{
return View();
}
}
OR you can override your authorization at controller level ,
Create a new OverrideAuthorizeAttribute attribute.
public class OverrideAuthorizeAttribute : AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
}
and you can use this attribute to override your controller level autorization.
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
[OverrideAuthorizeAttribute(Roles ="xxxx")] // This role will override controller
//level authorization
public ActionResult SomeOtherAction()
{
return View();
}
}
You need two authorization attributes - a base one with all authorization logic, and a second one, derived from the base attribute, that is only used to override the base attribute.
Example authorization attributes:
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
protected bool _canOverride = true;
//...custom authorization code goes here.....
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Don't authorize if the override attribute exists
if (_canOverride && actionContext.ActionDescriptor.GetCustomAttributes<OverrideClaimsAuthorizeAttribute>().Any())
{
return;
}
base.OnAuthorization(actionContext);
}
}
public class OverrideClaimsAuthorizeAttribute : ClaimsAuthorizeAttribute
{
public OverrideClaimsAuthorizeAttribute ()
: base()
{
_canOverride = false;
}
}
In the base authorization attribute we are saying to go ahead and authorize as normal, as long as the OverrideClaimsAuthorizeAttribute doesn't exist. If the OverrideClaimsAuthorizeAttribute does exist, then only run the authorization on classes where _canOverride is false (ie the OverrideClaimsAuthorizeAttribute class itself).
Example usage:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
//Ignores the controller authorization and authorizes with Roles=Administrators
[OverrideClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
//Runs both the controller and action authorization, so authorizes with Roles=Administrators AND Roles=AdvancedUsers
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdvancedAdministrativeTask()
{
return View();
}
//authorizes with controller authorization: Roles=AdvancedUsers
public ActionResult SomeOtherAction()
{
return View();
}
}
Check this previous question. (check #AndyBrown answer, case 2)
For a simple way you might also try adding (
[AllowAnonymous]) to override the controller
[Authorize]
then add a new custom filter to check for your logic for this particular action. Or you can add the code that checks for the role just inside it.
I have an ASP.NET MVC website that I would like to add a small administration page to. The issue I have is that I will be deploying this all over and I will not have SSL available. I am OK with requiring the administrator to remote desktop and use the local browser to perform the administration.
Can this be done? I would basically like to get the same behavior as <customeErrors mode="RemoteOnly" /> except for my administration pages. Can I do this via web.config some how?
Request.IsLocal is your friend.
http://msdn.microsoft.com/en-us/library/system.web.httprequest.islocal.aspx
You can use that to check that a request is coming from the local machine.
Custom Attribute
You could then extend this to be a custom attribute, but that might be overkill. If that is the route you choose this is a good example that does something similar:
Custom Attributes on ActionResult
MVC3 onwards allows you to set an attribute at Controller level, rather than Method too, so you could lock access to the entire controller responsible for the admin pages.
I did it by writing a custom attribute, like this:
public class IsLocalAttribute : AuthorizeAttribute
{
public bool ThrowSecurityException { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isLocal = httpContext.Request.IsLocal;
if (!isLocal && ThrowSecurityException)
throw new SecurityException();
return isLocal;
}
}
Basic usage on an entire controller:
[IsLocal]
public class LocalOnlyController : Controller
{
public ActionResult Index()
{
return View();
}
}
or on a specific method:
public class SomeController : Controller
{
[IsLocal]
public ActionResult LocalOnlyMethod()
{
return View();
}
}
If you want to throw a security exception instead of a 302 redirect:
public class SomeController : Controller
{
[IsLocal(ThrowSecurityException = true)]
public ActionResult LocalOnlyMethod()
{
return View();
}
}