Apply different authorization policy on API depending on Http Method

55 views Asked by At

I have an API developed in .NET7 with multiple controllers. Right now all the actions have the same authorization policy so we accomplished it by setting it up in the useendpoints middleware like below

app.UseEndpoints(endpoints =>
{   
    endpoints.MapDefaultControllerRoute().RequireAuthorization("MyPolicy")
});

But now we are trying to have separate policies for GET and POST's. Is there a way to dynamically achieve this with out decorating each method with a different Authorize("Policy") attribute. Thank you in Advance

1

There are 1 answers

0
Ruikai Feng On

A work around for you: create a route constraint:

public class NotPostConstraint : IRouteConstraint
    {
        

        public bool Match(
            HttpContext? httpContext, IRouter? route, string routeKey,
            RouteValueDictionary values, RouteDirection routeDirection)
        {
            if (httpContext?.Request.Method == HttpMethod.Post.Method)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }

configure as below in your program.cs:

builder.Services.AddRouting(options =>
{
    options.ConstraintMap.Add("notpost", typeof(NotPostConstraint));
    
});
......
  // all your Post request won't get into the endpoints here
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller}/{action:notpost}/{id?}",
        defaults: new { controller = "Home", action = "Index" }
        );
    
    app.MapControllerRoute(
        name: "default2",
        pattern: "{controller}/{action}/{id?}",
        defaults: new {controller="Home",action="Index"}
        ).RequireAuthorization(new AuthorizeAttribute() {AuthenticationSchemes="Cookies",Policy="MyPolicy" });

another workaround:

create a middleware replace the default authorize middleware:

public class HttpMethodBasedAuthorizationMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // Check the HTTP method
        var httpMethod = context.Request.Method;
 
        if (httpMethod == HttpMethods.Get)
        {
            var authorizationService = context.RequestServices.GetRequiredService<IAuthorizationService>();
            var authorizationResult = await authorizationService.AuthorizeAsync(context.User, null, "ReadPolicy");
 
            if (!authorizationResult.Succeeded)
            {
                context.Response.StatusCode = StatusCodes.Status403Forbidden;
                return;
            }
        }
        else if (httpMethod == HttpMethods.Post || httpMethod == HttpMethods.Put || httpMethod == HttpMethods.Delete)
        {
            var authorizationService = context.RequestServices.GetRequiredService<IAuthorizationService>();
            var authorizationResult = await authorizationService.AuthorizeAsync(context.User, null, "WritePolicy");
 
            if (!authorizationResult.Succeeded)
            {
                context.Response.StatusCode = StatusCodes.Status403Forbidden;
                return;
            }
        }
 
        await next(context);
    }
}