Custom roles on an Authorize attribute in MVC4 - asp.net-mvc-4

Here is my code:
[HttpGet, Authorize(Roles = "Admin")]
public ActionResult ActivityLog()
{
'code to do stuff
return View(model);
}
It's pretty simple - if you are in the "Admin" role you can get into this action. However I have a custom ActionFilter that populates my IPrinciple with all the custom claims (I cant use ADFS to send the claims because I have ONE ADFS for multiple sites so my claims have to be for that specific site).
public class CustomFilter: ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
'go get custom claims
}
}
}
I tie the custom filter into the application from the Global.asax file
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomFilter());
}
The problem is since the Authorize attribute runs before my custom filter I don't have the
"Admin" role and i get a 401 - Unauthorized Access error. How do I still keep the filter AND use the "Roles" tag in the Authorize attribute?

In regards to
"The problem is since the Authorize attribute runs before my custom
filter I don't have the "Admin" role"
You can create a another Authorize attribute which access the claims first, and then your standard Authorization which sets up the Admin.
The way you do this is to register and specify the Order property
filters.Add(new AuthorizeAttribute(), 1);
filters.Add(new CustomAuthorizeAttribute(), 2);
See more information on Filter Ordering

AuthorizeAttribute is no longer supported in MVC 4. You should now use the new AllowAnonymous attribute.
http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx
ASP.NET MVC 4 includes the new AllowAnonymous attribute, you no longer need to write that code. Setting the AuthorizeAttribute globally in global.asax and then whitelisting (That is, explicitly decorating the method with the AllowAnonymous attribute) the methods you want to opt out of authorization is considered a best practice in securing your action methods.
If you attempt to use an action filter and override AuthorizeCore, you'll get a compile time error "There is no suitable method for override".
Here is another method of performing attribute authorization in MVC 4:
public class AuthAttribute : AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
HandleUnauthorizedRequest(actionContext);
}
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Redirect);
response.Headers.Add("Location", "http://www.google.com");
actionContext.Response = response;
}
}

Related

Evaluate AuthorizeAttribute in action

One can authorize an action by using the [Authorize] attribute. But I want to only perform authorization on the action in specific conditions, so I cannot use this attribute. I don't think I am able to use IAuthorizationService.AuthorizeAsync as I don't have any policy names. Here's my service configuration in Startup.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration, "AzureAdB2C");
So my question is, how can I move the [Authorize] evaluation into the action code?
The AuthorizeAttribute will be converted (maybe with others) into an AuthorizeFilter and this filter will be executed with some code that is not equivalent to the simple IAuthorizationService.AuthorizeAsync. But if you want to use that anyway, we can get the default policy (which is used by [Authorize] without any policy specified) by using the IAuthorizationPolicyProvider.GetDefaultPolicyAsync. After that you can authorize the User to get an AuthorizationResult. It is succeeded if the property Succeeded is true. Otherwise, you can have the detailed failure in the property Failure (of type AuthorizationFailure). Here's the code:
public class TestController {
readonly IAuthorizationService _authorizationService;
readonly IAuthorizationPolicyProvider _authorizationPolicyProvider;
public TestController(IAuthorizationService authorizationService,
IAuthorizationPolicyProvider authorizationPolicyProvider){
_authorizationService = authorizationService;
_authorizationPolicyProvider = authorizationPolicyProvider;
}
public async Task<IActionResult> SomeAction(){
var defaultPolicy = await _authorizationPolicyProvider.GetDefaultPolicyAsync();
var authResult = await _authorizationService.AuthorizeAsync(User, defaultPolicy);
if(authResult.Succeeded){
//do something ...
}
}
}

Custom attribute validation in ASP.NET Core is running before JWT token is processed

asp.net core authorization
I am trying to use a custom authorization attribute to have finer control over my controller actions like this (somewhat similar to How do you create a custom AuthorizeAttribute in ASP.NET Core?)
[MyCustomAuth(Permissions="Products/Read")]
public IActionResult SomeMethod()
{
.....
}
public class MyCustomAuthAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public string Permissions { get; set; } //Permission string to get from controller
public void OnAuthorization(AuthorizationFilterContext context)
{
//
//read jwttoken
//and process permissions string
//to decide if user can run controller method
//
..
}
}
Unfortunately the JWT authorization handler that is built into ASP.NET core (configured in startup.cs) is run only AFTER this custom attribute is code is run so I can't seem to access the JWT token and THEN process the custom auth parameters.
Is there anyway to force the JWT token to be processed first and then do an extra validation using the custom attribute?
I think I found a solution...it seems to work..but could someone please confirm this is the right way?
Just implement IOrderedFilter interface and set Order to a high number. This means JWT authentication will be called first and then your custom authorization filter.
public class MyCustomAuthAttribute : AuthorizeAttribute, IAuthorizationFilter
{
...
public int Order => 9999;
...
}

How to get the currently logged in user name through an interface to insert in updated by column in asp.net core mvc?

I want to create a column updated by so whenever a user modifies a data, the updated by column is filled with currently logged in user name. I want to use an interface that gets the name of currently logged in user and then in AppDbContext in savechanges method the username is inserted whenever the data is updated. Please someone help me out how to do it as I'm new to ASP.NET core mvc and I got an assignment from University. I will be really thankful to all the experienced programmers here.
I hope I understood your question well. Here it goes:
If you want to retrieve some information about the user that sent a request to your server and you use default identity behavior, then it should be easy. I do not know what version of ASP.NET do you use, I will presume its ASP.NET Core 3 (from you tags and such).
With each request, there is a HttpContext object that remembers information about the HTTP request, the HTTP response, etc. If you inherit your controllers from the Controller/ControllerBase, you can directly access that context. The context contains a property called "User" that contains various information, including your user name.
[AllowAnonymous]
[HttpGet("me")]
public string WhoAmI()
{
//Try this
var userName = HttpContext.User?.Identity?.Name;
//Or even
var userName = User?.Identity?.Name;
return userName;
}
Now, how can you get this name for your AppDbContext context? First of all, the best approach would be to create a service that provides you this name. Let's call it UserRepository. The user repository will accept IHttpContextAccessor that provides you with the HttpContext (and this username). The IHttpContextAccessor will be injected if you use dependency injection (used "automatically" within ASP.NET).
public class UserRepository : IUserRepository
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserRepository(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void GetCurrentUserName()
{
var username = _httpContextAccessor.HttpContext.User.Identity.Name;
return username;
}
}
You can add this service to your services and you can inject it in various places. I should be even injectable to your DB context.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(); //You probably have this one
services.AddHttpContextAccessor(); //Add the context accessor
services.AddTransient<IUserRepository, UserRepository>(); //This is our user repository
}
And you should be able to use it as:
public class AppDb : DbContext
{
readonly IUserRepository userRepo;
public AppDb(DbContextOptions<AppDb> options,
IUserRepository userRepo)
: base(options)
{
this.userRepo = userRepo;
userRepo.GetCurrentUserName();
}
}
The IUserRepository will "automatically" get injected with each request.
Or, you can just inject the IUserRepository wherever you need. For example in the constructor of a Controller, similarly as shown in the AppDb object. Not sure what you really need.

How to implement Handler level authorization in asp.net core RazorPages

Authorize attribute at PageModel is not enough because it applies to all handlers in that pagemodel.
I am looking for some custom authorization lets say "Can access only if age is 18+", so how to implement same in RazorPages (Not MVC)?
Is there any neat and clear solution for this??
Currently it is not possible to use Authorize attribute on individual Razor Page handlers , you can trace the feature request in here .
As a workaround , you can inject the IAuthorizationService and implement the authorization manually :
private readonly IAuthorizationService _authorizationService;
public IndexModel(ILogger<PrivacyModel> logger, IAuthorizationService authorizationService)
{
_logger = logger;
_authorizationService = authorizationService;
}
public async Task OnGet()
{
var isAuthorized = await _authorizationService.AuthorizeAsync(
User, "PolicyName");
if (!isAuthorized.Succeeded)
{
//not pass the authorzation
}
}
And create policy to check whether the value user's age claim is over 18 as shown here .

ASP.Net/MVC Authorize Vs Authenticate

So I set this above my Controller:
[Authorize(Roles="Administrator")]
The problem is whether they are not logged in, or don't have the right role, it redirects them to the login page. Is there a way to have it handle authorization and authenticate differently?
I might not understand you clearly, but authentication and authorization are always coming together.. One says which mechanism use to authenticate user (forms, windows etc.), and second which roles or users are allowed to see the content...
As far as authentication method is set in your web config it is fixed, and only think you can use to protect your controller methods is to put those attributes.
Also if you want to use it diffrently, f.e. redirect to diffrent page you can use following code:
public class RedirectAuthorizeAttribute : AuthorizeAttribute
{
public string RedirectUrl { get; set; }
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectResult(RedirectUrl);
}
}
and then put it onto your controller method like that:
[RedirectAuthorize(Roles = "MyRole", RedirectUrl = "SomeUrl")]
public ActionResult SomeAction()
{
...
}