Is there a way to require a specific Authorization Scheme when using the [Authorize] Attribute on a Controller in asp.net MVC 4?
I expected something like this (which is totally possible in .net core btw)
[Authorize(AuthenticationSchemes = "Bearer")]
public class MyController : Controller { }
As far as I know, there is nothing out of the box that would allow you to write this.
The standard authorize attribute doesn't support this.
But you could write your own attribute and check the claims of the identity coming in.
I used an backport of ASP.NET Core authorization policies to .NET Full framework: https://github.com/DavidParks8/Owin-Authorization to write such rules.
How to check of you come from which token?
Normally you will see a claim similar to "idp": "oidc"
How to get the claims? ((ClaimsPrinciple)User).Claims ( in Controller code)
As suggested by #Chetan Ranpariya in the comments I ended up implementing a derived attribute (from AuthorizeAttribute). According to the documentation, overriding the AuthroizeCore method is the way to do it.
When overridden, provides an entry point for custom authorization checks.
Here is a working example for future reference
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public string AuthSchemes { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (this.AuthSchemes != null)
{
string scheme = httpContext?.User?.Identity?.AuthenticationType;
if (string.IsNullOrWhiteSpace(scheme))
{
return false;
}
return this.AuthSchemes.Split(',').Contains(scheme);
}
return base.AuthorizeCore(httpContext);
}
}
The attribute can be used like this
[MyAuthorize(AuthSchemes = CookieAuthenticationDefaults.AuthenticationType)]
public class MyController : Controller { }
Related
For some experimental/exercise purposes, I'm trying to implement a primitive login system before proceeding to Identity services.
My idea was creating a custom data annotation -just like [Authorize] of Identity- and passing inputted form data carried by Session[]. Hence, I would use my custom annotation to authorize certain controllers or methods.
public class IdCheck: ValidationAttribute
{
public string currentId { get; set; }
public override bool IsValid(object value)
{
currentId = value as string;
if (currentId != null)
{
return true;
}
else
{
return false;
}
}
}
and I tried to use it like that:
[IdCheck(currentId = Session["UserId"])]
public class SomeController : Controller
{
//methods
}
However, I got an error with the message: "An object reference is required for the non static field, method or property 'Controller.Session'"
I can neither access the Session[] before a controller nor before method declaration. I have no idea how to instantiate or refere.
Is there a way to utilize Session[] in my case? Because I don't want to manually check each method with if statements like
if(Session["UserId"] != null) {}
Any alternative solution is more than welcomed.
Note: I am aware of that using Session is not a recommended way of implementing such a task. But my intention is to have an understanding of how login operations work before utilizing advanced Identity services.
Is there a way to utilize Session[] in my case?
Not as you're doing now, but you could use HttpContext.Current.Session inside your custom attribute code instead of passing it as property.
Thanks a lot Oscar. What you proposed actually worked. However, I managed to find a better way yo carry out my "primitive login" operation using a custom data annotation.
After some more research it turned out that implementing FilterAttribute class along with IAuthorizationFilter filter does the trick. I believe this could help someone else as well:
public class IdCheck : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Session["UserId"] == null)
{
filterContext.Result = new RedirectResult("/");
}
}
}
and the usage is the same of course:
[IdCheck]
public class SomeController : Controller
{
//methods
}
I have a database with users, the users can have different roles.
I want to implement basic authentication for my MVC Web API and I want to be able to tag methods with an Authorize tag and also pass userType as a parameter.
[Authorize(admin)]
public bool Test()
{
}
[Authorize(user)]
public bool Test1()
{
}
I can't figure out how to make this attribute, for example how do I make an attribute that simply makes a method always return false?
[AttributeUsage(AttributeTargets.All)]
public class TestAttribute: System.Attribute
{
//Return false?
}
I'm looking for some advice.
EDIT:
I made the following class:
ilterContext.HttpContext.Response.StatusCode = 401;
filterContext.Result = new EmptyResult();
filterContext.HttpContext.Response.End();
}
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
then i add [BasicAuthorize] to a method, but it still let me access it without basic autentication.
any idea?
I suggest you read some basics about authorization and authentication and how the Authorize attribute can be applied.
Roles are out of the box. Just use the built-in capabilities of the Authorize attribute: Example:
[Authorize(Roles="admin")]
public bool Test()
{
}
If you need a custom implementation you need to inherit from System.Web.Mvc.AuthorizeAttribute instead of from System.Attribute. Everything else is pretty straight forward from here. Basic example explained here: Basic Authorization Attribute in ASP.NET MVC
I am trying to set up authorization in ASP.NET Core 1.0 (MVC 6) web app.
More restrictive approach - by default I want to restrict all controllers and action methods to users with Admin role. So, I am adding a global authorize attribute like:
AuthorizationPolicy requireAdminRole = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireRole("Admin")
.Build();
services.AddMvc(options => { options.Filters.Add(new AuthorizeFilter(requireAdminRole));});
Then I want to allow users with specific roles to access concrete controllers. For example:
[Authorize(Roles="Admin,UserManager")]
public class UserControler : Controller{}
Which of course will not work, as the "global filter" will not allow the UserManager to access the controller as they are not "admins".
In MVC5, I was able to implement this by creating a custom authorize attribute and putting my logic there. Then using this custom attribute as a global. For example:
public class IsAdminOrAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
ActionDescriptor action = filterContext.ActionDescriptor;
if (action.IsDefined(typeof(AuthorizeAttribute), true) ||
action.ControllerDescriptor.IsDefined(typeof(AuthorizeAttribute), true))
{
return;
}
base.OnAuthorization(filterContext);
}
}
I tried to create a custom AuthorizeFilter, but no success. API seems to be different.
So my question is: Is it possible to set up default policy and then override it for specific controllers and actions. Or something similar.
I don't want to go with this
[Authorize(Roles="Admin,[OtherRoles]")]
on every controller/action, as this is a potential security problem. What will happen if I accidentally forget to put the Admin role.
You will need to play with the framework a bit since your global policy is more restrictive than the one you want to apply to specific controllers and actions:
By default only Admin users can access your application
Specific roles will also be granted access to some controllers (like UserManagers accessing the UsersController)
As you have already noticied, having a global filter means that only Admin users will have access to a controller. When you add the additional attribute on the UsersController, only users that are both Admin and UserManager will have access.
It is possible to use a similar approach to the MVC 5 one, but it works in a different way.
In MVC 6 the [Authorize] attribute does not contain the authorization logic.
Instead the AuthorizeFilter is the one that has an OnAuthorizeAsync method calling the authorization service to make sure policies are satisfied.
A specific IApplicationModelProvider is used to add an AuthorizeFilter for every controller and action that has an [Authorize] attribute.
One option could be to recreate your IsAdminOrAuthorizeAttribute, but this time as an AuthorizeFilter that you will then add as a global filter:
public class IsAdminOrAuthorizeFilter : AuthorizeFilter
{
public IsAdminOrAuthorizeFilter(AuthorizationPolicy policy): base(policy)
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext context)
{
// If there is another authorize filter, do nothing
if (context.Filters.Any(item => item is IAsyncAuthorizationFilter && item != this))
{
return Task.FromResult(0);
}
//Otherwise apply this policy
return base.OnAuthorizationAsync(context);
}
}
services.AddMvc(opts =>
{
opts.Filters.Add(new IsAdminOrAuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("admin").Build()));
});
This would apply your global filter only when the controller/action doesn't have a specific [Authorize] attribute.
You could also avoid having a global filter by injecting yourself in the process that generates the filters to be applied for every controller and action. You can either add your own IApplicationModelProvider or your own IApplicationModelConvention. Both will let you add/remove specific controller and actions filters.
For example, you can define a default authorization policy and extra specific policies:
services.AddAuthorization(opts =>
{
opts.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().RequireRole("admin").Build();
opts.AddPolicy("Users", policy => policy.RequireAuthenticatedUser().RequireRole("admin", "users"));
});
Then you can create a new IApplicatioModelProvider that will add the default policy to every controller that doesn't have its own [Authorize] attribute (An application convention would be very similar and probably more aligned with the way the framework is intended to be extended. I just quickly used the existing AuthorizationApplicationModelProvider as a guide):
public class OverridableDefaultAuthorizationApplicationModelProvider : IApplicationModelProvider
{
private readonly AuthorizationOptions _authorizationOptions;
public OverridableDefaultAuthorizationApplicationModelProvider(IOptions<AuthorizationOptions> authorizationOptionsAccessor)
{
_authorizationOptions = authorizationOptionsAccessor.Value;
}
public int Order
{
//It will be executed after AuthorizationApplicationModelProvider, which has order -990
get { return 0; }
}
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
foreach (var controllerModel in context.Result.Controllers)
{
if (controllerModel.Filters.OfType<IAsyncAuthorizationFilter>().FirstOrDefault() == null)
{
//default policy only used when there is no authorize filter in the controller
controllerModel.Filters.Add(new AuthorizeFilter(_authorizationOptions.DefaultPolicy));
}
}
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
//empty
}
}
//Register in Startup.ConfigureServices
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, OverridableDefaultAuthorizationApplicationModelProvider>());
With this in place, the default policy will be used on these 2 controllers:
public class FooController : Controller
[Authorize]
public class BarController : Controller
And the specific Users policy will be used here:
[Authorize(Policy = "Users")]
public class UsersController : Controller
Notice that you still need to add the admin role to every policy, but at least all your policies will be declared in a single startup method. You could probably create your own methods for building policies that will always add the admin role.
Using #Daniel's solution I ran into the same issue mentioned by #TarkaDaal in the comment (there's 2 AuthorizeFilter in the context for each call...not quite sure where they are coming from).
So my way to solve it is as follow:
public class IsAdminOrAuthorizeFilter : AuthorizeFilter
{
public IsAdminOrAuthorizeFilter(AuthorizationPolicy policy): base(policy)
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
{
if (context.Filters.Any(f =>
{
var filter = f as AuthorizeFilter;
//There's 2 default Authorize filter in the context for some reason...so we need to filter out the empty ones
return filter?.AuthorizeData != null && filter.AuthorizeData.Any() && f != this;
}))
{
return Task.FromResult(0);
}
//Otherwise apply this policy
return base.OnAuthorizationAsync(context);
}
}
services.AddMvc(opts =>
{
opts.Filters.Add(new IsAdminOrAuthorizeFilter(new AuthorizationPolicyBuilder().RequireRole("admin").Build()));
});
This is ugly but it works in this case because if you're only using the Authorize attribute with no arguments you're going to be handled by the new AuthorizationPolicyBuilder().RequireRole("admin").Build() filter anyway.
I have the following code:
CookieHeaderValue cookie = Request.Headers.GetCookies("session").FirstOrDefault();
var isAuthenticated = _userService.IsAuthenticated(cookie);
if (!isAuthenticated)
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "");
I'd like this code to execute as soon as any part of my api is called. I havn't found any good solutions or ways to do this so i thought i would ask here instead.
(what I do now is execute the code in every get/post/put/delete which is horrible).
The best place to solve this would be an authorization filter attribute. See Authentication Filters in ASP.NET Web API 2.
The subject is too broad to repeat here in its entirety, but it comes down to creating an attribute:
public class CookieAuthenticationFilterAttribute : Attribute, IAuthenticationFilter
{
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
// your cookie code
}
}
And applying it to the controller or action methods:
[YourCookieAuthentication]
But be sure to read the link.
You can use an ActionFilter or AuthorizationFilter for this purpose. These are attribute classes that you can use on specific controllers/actions or globally. So you don't need to repeat the code for every action.
See this link for details. It shows the general authentication/authorization flow in ASP.NET Web API and how you can customize it.
So i found the best solution for my problem was the following code:
public class CookieFilterAttribute : AuthorizeAttribute
{
[Inject]
public IUserService UserService { get; set; }
protected override bool IsAuthorized(HttpActionContext actionContext)
{
CookieHeaderValue cookie = actionContext.Request.Headers.GetCookies("session").FirstOrDefault();
var isAuthenticated = UserService.IsAuthenticated(cookie);
return isAuthenticated;
}
}
I'm having some problem with my custom AuthorizeAttribute
public class ExplicitAuthorizeAttribute : AuthorizeAttribute
{
private readonly MembershipUserRole[] _acceptedRoles;
public ExplicitAuthorizeAttribute()
{
}
public ExplicitAuthorizeAttribute(params MembershipUserRole[] acceptedRoles)
{
_acceptedRoles = acceptedRoles;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Validation ...
}
}
I use it like this:
[ExplicitAuthorize[(MembershipUserRole.Admin, MembershipUserRole.SuperAdmin)]
It works perfectly for HttpGet and HttpPost to validate my controllers and methods.
But when I use it in a ApiController and make ajax calls, AuthorizeCore isn't running and I got a security breach. :/
My enum looks like this
[Flags]
public enum MembershipUserRole
{
Admin= 1,
SuperAdmin = 2
}
Does anyone know why my AuthorizeCore isn't validating in this context?
By the way If I use
[Authorized(Roles ="Admin, SuperAdmin")]
It's validates perfectly, but I'd like to have Stronly Typed Roles,that's why I'm using enums.
You have derived from the wrong class: System.Web.Mvc.AuthorizeAttribute whereas for a Web API controller you should derive from System.Web.Http.AuthorizeAttribute.
Don't forget that ASP.NET MVC and ASP.NET Web API are 2 completely different frameworks and even if they share some common principles and names, the corresponding classes are located in 2 completely different namespaces.
So what you have done is decorate an ASP.NET Web API action with an AuthorizeAttribute that it doesn't know anything about.
If you want to make authorization in ASP.NET Web API make sure you have derived from the correct attribute:
public class ExplicitAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
private readonly MembershipUserRole[] _acceptedRoles;
public ExplicitAuthorizeAttribute()
{
}
public ExplicitAuthorizeAttribute(params MembershipUserRole[] acceptedRoles)
{
_acceptedRoles = acceptedRoles;
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
//Validation ...
}
}