"context.Resource as AuthorizationFilterContext" returning null in ASP.NET Core 3.0

This is due to the new endpoint routing in .NET Core 3.0.

To quote the below ticket.

This is because when using endpoint routing in ASP.NET Core 3.0:

Mvc will no longer add AuthorizeFilter to ActionDescriptor and ResourceInvoker will not call AuthorizeAsync() https://github.com/aspnet/AspNetCore/blob/90ab2cb965aeb8ada13bc4b936b3735ca8dd28df/src/Mvc/Mvc.Core/src/ApplicationModels/AuthorizationApplicationModelProvider.cs#L40

Mvc will add all Filter as metadata to endpoint.Metadata https://github.com/aspnet/AspNetCore/blob/5561338cfecac5ca4b1dda2f09a7f66153d0b5fe/src/Mvc/Mvc.Core/src/Routing/ActionEndpointFactory.cs#L348

instead by AuthorizationMiddleware call the AuthorizeAsync() and resouorce is Endpoint https://github.com/aspnet/AspNetCore/blob/5561338cfecac5ca4b1dda2f09a7f66153d0b5fe/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs#L63

New method.

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CookieOrTokenAuthorizationRequirement requirement)
{
    if (context.Resource is Endpoint endpoint)
    {
        if (endpoint.Metadata.OfType<IFilterMetadata>().Any(filter => filter is MyFilter))
        {
            context.Succeed(requirement);
            return Task.CompletedTask;
        }
    }
}

https://github.com/dotnet/aspnetcore/issues/11075

It's worth noting too that using the new context you won't be able to access route data as you were before with the AuthorizationFilterContext. You will need to inject an IHttpContextAccessor into the AuthorizationHandler.

// Ensure your handler is registered as scoped
services.AddScoped<IAuthorizationHandler, InvestorRequirementHandler>();


public class InvestorRequirementHandler : AuthorizationHandler<InvestorRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public InvestorRequirementHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, InvestorRequirement requirement)
    {
        var tenant = httpContextAccessor.HttpContext.GetRouteData().Values[ExceptionHandlerMiddleware.TenantCodeKey].ToString();
    }
}

public class CanEditOnlyOtherAdminRolesAndClaimsHandler :
            AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
{
    private readonly IHttpContextAccessor httpContextAccessor;
    public CanEditOnlyOtherAdminRolesAndClaimsHandler(
            IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;    
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
            ManageAdminRolesAndClaimsRequirement requirement)
    {

        var loggedInAdminId = context.User.Claims
            .FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value.ToString();

        var adminIdBeingEdited = httpContextAccessor.HttpContext
            .Request.Query["userId"].ToString();

        if (context.User.IsInRole("Admin")
             && context.User.HasClaim(c => c.Type == "Edit Role" && c.Value == "true")
             && adminIdBeingEdited.ToLower() != loggedInAdminId.ToLower())
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }

}

Tags:

C#

Asp.Net