I need to add authorization to a particular route without adding Authorize attribute. Is there any way I can do this in startup? I know I can add Authorize attribute globally to all the routes, but I need to add authorize just to a specific method in a controller without touching any code in that controller.
If you cannot touch code I see the only solution - check using middleware. Lets imagine that route you want to restrict access is POST '/users/register', so you can use ActionFilter registered globally in startup in which you check url and if its url is '/users/register' you are trying to check token and if token is not valid - return 401.
Also you can use Owin middleware
Here is simple example of implementation such logic using ActionFilter
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configuration.Filters.Add(new CheckAuthorizationFilterAttribute());
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
public class CheckAuthorizationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var requestUri = actionContext.Request.RequestUri.AbsolutePath;
if (requestUri == "/api/users/register")
{
var isTokenValid = ValidateToken();
if (!isTokenValid)
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
return;
}
}
public bool ValidateToken() => false;
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
}
}
Related
My objective is to return back HttpContext.TraceIdentifier in the response header for all the APIs.
The return type of all APIs is IActionResult.
I've seen solutions for creating a middlware and overriding messageHandler.
I want to do minimal changes to just return it back.
Currently, I'm not interfering with the default message handler.
As far as I know, you could try to use asp.net core action filter as global filter or as attribute to achieve your requirement.
You could get the httpcontext in the OnActionExecuted method and then you could register the filter for specific controller or as global filter.
More details, you could refer to below codes:
Create a action filter:
public class TestActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
var re = context.HttpContext.TraceIdentifier;
context.HttpContext.Response.Headers.Add("TraceIdentifier", re);
}
public void OnActionExecuting(ActionExecutingContext context)
{
//throw new NotImplementedException();
}
}
Add it as attribute in web api controller:
[TypeFilter(typeof(TestActionFilter))]
public class WeatherForecastController : ControllerBase
Or register it as global filter:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options=>options.Filters.Add(typeof(TestActionFilter)));
}
Result:
I have written custom attribute to redirect request to https when http is requested for my Web API project as below
public class RedirectToHttpAttribute: AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
var response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Found, "");
var uri = new UriBuilder(actionContext.Request.RequestUri);
uri.Scheme = Uri.UriSchemeHttps;
uri.Port = 44326;
response.Headers.Location = uri.Uri;
actionContext.Response = response;
}
}
}
Now I want to set this attributes to all my controllers and actions so I added this in WebApiConfig.
config.Filters.Add(new RedirectToHttpAttribute());
Now there is one controller where I need to allow both http and https. To make this possible I have to remove above line from WebApiConfig and add to all controllers except one in the question. I can do it easily as I have very few controllers but if I had many many controller then what would have been the solution as it is very likely to generate error prone to decorate each controller?
You could do this by creating a second attribute, and modifying your existing redirection filter.
Something like this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class AllowHttpAttribute : Attribute
{
}
public class RedirectToHttpsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionDescriptor.GetCustomAttributes<AllowHttpAttribute>(false).Any())
{
return;
}
// Perform the redirect to HTTPS.
}
}
Then on your controller (or action):
[AllowHttp]
public class ValuesController : ApiController
{
// ...
}
I would like to implement Anti-CSRF token in Global.asax file of MVC 3.
Is that possible to implement the same in Gloabl.asax file.
Seems what you need is to create a custom filter class which implements IAuthorizationFilter for all POST methods, by checking HttpContext.Request.HttpMethod request:
public class ValidateAntiForgeryTokenEveryPost : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext context)
{
if (context.HttpContext.Request.HttpMethod == "POST")
{
System.Web.Helpers.AntiForgery.Validate();
}
}
}
Then, add the new filter in FilterConfig class:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ValidateAntiForgeryTokenEveryPost());
}
}
Also ensure that the custom filter has registered in Global.asax code:
protected void Application_Start()
{
// other settings
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
// other settings
}
By using global filtering given above, all POST method requests are automatically checks for AntiForgeryToken, no matter if #Html.AntiForgeryToken() is not present inside view pages.
Addendum 1:
It is possible to exclude certain actions from CSRF token checking, what you need is preventing Validate method to execute while a custom attribute class is present. First, create a custom attribute class for validation check:
[AttributeUsage(AttributeTargets.Method)]
public class ExcludeAntiForgeryCheckAttribute : Attribute
{
// other stuff
}
Afterwards, use ActionDescriptor.GetCustomAttributes to get custom attribute type created above:
public class ValidateAntiForgeryTokenEveryPost : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext context)
{
// adapted from Darin Dimitrov (/a/34588606/)
bool isValidate = !context.ActionDescriptor.GetCustomAttributes(typeof(ExcludeAntiForgeryCheckAttribute), true).Any();
// use AND operator (&&) if you want to exclude POST requests marked with custom attribute
// otherwise, use OR operator (||)
if (context.HttpContext.Request.HttpMethod == "POST" && isValidate)
{
System.Web.Helpers.AntiForgery.Validate();
}
}
}
Then you can decorate any methods which should be exempted from CSRF validation token:
[HttpPost]
[ExcludeAntiForgeryCheck]
public ActionResult Index(ViewModel model)
{
// other stuff
return View(model);
}
References:
Check CRSF token by default in ASP.NET MVC (standard version)
Securing all forms using AntiForgeryToken (attribute-based version)
I don't think so. For each request we need to check token.
Please try to use below code in view file.
#Html.AntiForgeryToken()
I am using SelfHost/Katana/Owin for my WebServer.
I have a Controller in there that I want to enable/disable by code depending on a command line argument at launch time.
Is there a simple way of doing this in MVC?
Right now I'm thinking in the Controller's code to return HTTP-NotFound status code when this config is disabled, any better ideas?
You could decorate your controller with a custom Action Filter.
public class ConfigActionFilter : ActionFilterAttribute {
// This method is called before a controller action is executed.
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if(someConfigSetting) {
filterContext.Result = new RedirectToRouteResult("Error", someRouteValues);
}
}
...
}
Usage:
[ConfigActionFilter]
public class MyController : Controller {
...
}
More here.
You could perform a redirecttoaction that will take users to a different controller explaining what's happening.
ie:
public class MyController : Controller {
private IConfigReader _configReader;
public MyController(IConfigReader configReader){ //not sure if you're doing dependency injection or not, so I'm injecting it
_configReader = configReader;
}
public ActionResult Index() {
if(!_configReader.IsEnabled) {
return RedirectToAction("Denied", "AuthController");
}
//etc
return View();
}
}
You could create an attribute, apply it to the controller and set a static property on that attribute at startup time, and deny access (or return "Not found") when the flag is set.
Alternatively, you can implement a custom AuthorizationAttribute and put it on your controller
public class AuthorizationAdminAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (/*check for argument*/)
{
return false;
}
return true;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (AuthorizeCore(filterContext.HttpContext))
{
// ** 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(CacheValidateHandler, null /* data */);
}
else
{
filterContext.Result = new HttpNotFoundResult();
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}
I have a App that need authorization to access all Controllers/Actions. Except the Login and Error Controllers/Actions.
With this scenario, working in a defensive manner is better to keep default restrict access to all Controllers/Actions(without Authorize Attribute) and select with a custom Attribute only those who do not.
Have you guys done something like this?
I have a MVC Filter that execute before all Actions if the Logged User have access to them:
public class ValidatePermissionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
bool isAuthorized = false;
//Logic that verify if logged user have permission to access the requested Controller/Action
...
//Redirect to a page Error if Logged User don't have Authorization
if (!isAuthorized)
{
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", "Erro");
redirectTargetDictionary.Add("controller", "Index");
context.Result = new RedirectToRouteResult(redirectTargetDictionary);
}
}
}
I'm thinking the best way to do this. I can create a Blank Custom Attribute and put in the Controllers do not need authorization and check it in my Filter:
public class ValidatePermissionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
bool isAuthorized = false;
var DoNotRequiresAuthorizationAttributes = context.ActionDescriptor.GetCustomAttributes(typeof(DoNotRequiresAuthorizationAttribute), false);
if (DoNotRequiresAuthorizationAttributes.Length > 0)
isAuthorized = true;
...
//Redirect to a page Error if Logged User don't have Authorization
if (!isAuthorized)
{
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", "Erro");
redirectTargetDictionary.Add("controller", "Index");
context.Result = new RedirectToRouteResult(redirectTargetDictionary);
}
}
}
What you expert Guys think?
Update:
Thinking better, I can replace my Filter with a Custom Authorize Attribute and register that to act in all Controllers/Actions in Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyCustomAuthorizeAttribute());
}
Update 2:
Instead create a Blank Custom Attribute and put in the Controllers do not need authorization I pass in Parameters of my Custom Authorize the Controllers do not need authorization (in Global.asax):
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ValidatePermissionAttribute("Login", "Erro"));
}
My Authorize Attribute:
public class ValidatePermissionAttribute : AuthorizeAttribute
{
...
public ValidatePermissionAttribute(params string[] optionalControllers)
{
_optionalControllers = optionalControllers;
}
...
}
Update 3:
Conditional Filters is the way to go.
Have you considered using Conditional Filters in ASP.NET MVC 3?
Several ways to handle bulk implementations of attributes:
Create a custom controller base class and implement there.
I think you can use the MVC filter's global filters collection: http://weblogs.asp.net/gunnarpeipman/archive/2010/08/15/asp-net-mvc-3-global-action-filters.aspx
I've been told before the issue with using a filter attribute is that the result can be cached via output caching, and then this wouldn't run. It's better to implement an IAuthorizationFilter interface (or AuthorizeAttribute class) and create an authorization filter instead.
If the goal is just to reduce the need to re-declare the attribute in many places, it seems one could achieve the same by creating one abstract AuthorizeController with the attribute, and any controller whose actions all require authorization can inherit that.