- Ensure that all endpoints use our custom attributes by using reflection to check for the presence of either the HasPermissionAttribute or HasNoPermissionAttribute attribute. If an endpoint does not have one of these attributes, throw an exception to prevent the application from starting.
Inspired by Anthon and Nick Chapsas' YouTube videos on how to extend/implement custom types in .NET, I created a custom authorization handler for ASP.NET Core minimal APIs.
This is useful for applications with dozens or hundreds of api endpoints, as it allows for fine-grained access control without the need to create a separate authorization policy for each endpoint.
The idea basically is quite simple, just register a single access policy then decorate our endpoints with the HasPermissionAttribute
custom attribute.
The HasPermissionAttribute
class is used to control access to an api endpoint based on the user's permissions. It passes the name of the created policy to the AuthorizeAttribute(string policy)
base constructor, which sets the policy to use as part of the authorization process. It also defines a Name property, which will hold the name of the permission required to access a given endpoint.
/// Represents the Authorize Attribute. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] internal sealed class HasPermissionAttribute : AuthorizeAttribute { public string Name { get; } internal const string HasPermissionPolicyName = "HasPermission"; public HasPermissionAttribute(string name) : base(HasPermissionPolicyName) { Name = name; } }
Next, create and register a CustomAuthorizationRequirement
in the policy we created. By default, it must inherit from the AuthorizationHandler<TRequirement>
class and the IAuthorizationRequirement
interface.
Use the CustomAuthorizationRequirement<>
class to define the custom logic to either succeed or fail an authorization, in our case we check if the user has the permission required to access a given endpoint otherwise fail the authorization.
Here is an example implementation:
public class HasPermissionAuthorizationRequirement : AuthorizationHandler, IAuthorizationRequirement { }
o.AddPolicy(HasPermissionAttribute.HasPermissionPolicyName, policyBuilder => { policyBuilder.Requirements.Add(new HasPermissionAuthorizationRequirement()); policyBuilder.AddAuthenticationSchemes(IdentityConstants.ApplicationScheme); });
Finally, add some extension methods to be able to use something like ``app.Map("",...).RequirePermission("PermissionName")`` instead of ``app.Map("",...).RequireAuthorization()``