Back to: ASP.NET Core Web API Tutorials
Custom Authorization Filter in ASP.NET Core Web API
In this article, I will discuss how to Create Custom Authorization Filters in ASP.NET Core Web API Applications with Examples. Please read our previous article discussing Authorization Filters in ASP.NET Core Web API Applications.
What is a Custom Authorization Filter?
In ASP.NET Core Web API, authorization filters are components that execute early in the HTTP request pipeline to determine whether a user has permission to access a specific API endpoint (a controller action method). This step occurs after authentication (which verifies the user’s identity) but before the actual controller method runs.
The framework provides a built-in [Authorize] attribute that allows us to enforce authorization based on simple criteria like roles, policies, or claims. For example, you might restrict an endpoint so that only users with the “Admin” role can access it.
However, many real-world applications require more complex and custom business rules that [Authorize] cannot directly express. This is where Custom Authorization Filters come into the picture.
Example: Cinema Hall Entry with Custom Age Filter
Imagine a cinema hall screening different movies. Some movies are rated 18+, so only adults can enter those screenings. Custom Authorization Filter is like a security guard at the entrance. Before allowing anyone in, the guard checks the age on their ID.
- If the visitor is 18 or older, the guard lets them in.
- If a visitor is under 18, entry is denied right at the gate, regardless of their ticket.
In ASP.NET Core, we can create a custom authorization filter that checks the user’s age (from their profile or token) before allowing access to an action/method. If the filter finds the user is under 18, it returns a 403 Forbidden response, blocking access before the action runs.
Example: Members-Only Flash Sale on an E-Commerce Site
An online store announces a two-hour flash sale that only paid members can access. When anyone clicks the special sale link, the site quickly checks two things:
- Are they logged in?
- Does their account have an active membership that hasn’t expired?
If both checks pass, the shopper sees the discounted products. If either check fails, the site instantly redirects them to a Membership Required page (or a payment screen) without loading any sale content. In ASP.NET Core, the Membership Check is a Custom Authorization Filter that runs before the action showing the sale items.
ASP.NET Core Web API Project Setup
First, create a new ASP.NET Core Web API Project with the name CustomAuthFilterDemo. We will use JWT (JSON Web Tokens) for authentication. Please install the following JwtBearer NuGet Package by executing the below command in the Package Manager Console.
- Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Add JWT Secret in Configuration:
Please modify the appsettings.json file as follows. Here, we are adding a key that will be used to generate and validate the JWT token. The SecretKey will also be used to sign JWT tokens. In production, use environment variables or secret managers for better security.
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "JwtSettings": { "SecretKey": "d3011f8b98bbc1aa1c4ff1a7d4864fc72d9ee150bd682cf4e612d6321f57821d" } }
Please use the following site to generate the JWT Secret key.
https://jwtsecret.com/generate
Configure JWT Authentication in the Program.cs:
Modify Program.cs as follows to add JWT authentication:
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; namespace CustomAuthFilterDemo { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Read the JWT secret key from the appsettings.json configuration file. // This key will be used to sign and validate JWT tokens. var jwtSettings = builder.Configuration.GetSection("JwtSettings"); var secretKey = jwtSettings["SecretKey"] ?? "d3011f8b98bbc1aa1c4ff1a7d4864fc72d9ee150bd682cf4e612d6321f57821d"; // Register MVC controllers with the application. // Also, configure JSON options to keep property names as defined in the C# models. builder.Services.AddControllers() .AddJsonOptions(options => { // Disable camelCase in JSON output, preserve property names as defined in C# classes options.JsonSerializerOptions.PropertyNamingPolicy = null; }); // Register Authentication with JWT Bearer scheme builder.Services.AddAuthentication(options => { // Set the default scheme used for authentication — this means how the app will try to authenticate incoming requests options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; // Set the default challenge scheme — this is how the app will challenge unauthorized requests options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { // Configure parameters for validating incoming JWT tokens options.TokenValidationParameters = new TokenValidationParameters { // Do NOT validate the issuer (the token's "iss" claim) ValidateIssuer = false, // Do NOT validate the audience (the token's "aud" claim) ValidateAudience = false, // Ensure the token's signature matches the signing key (to verify token integrity) ValidateIssuerSigningKey = true, // The key used to sign tokens — must match the key used to generate tokens IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)) // Use a symmetric key from configuration for token validation. }; }); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // Add authentication middleware to validate JWT tokens in incoming requests. app.UseAuthentication(); // MUST come before UseAuthorization // Add authorization middleware to check user permissions for accessing resources. app.UseAuthorization(); app.MapControllers(); app.Run(); } } }
Create the User Model
First, create a folder named Models at the project root directory. Then, inside the Models folder, add a class named User.cs and copy and paste the following code. We will store the user list in memory objects.
namespace CustomAuthFilterDemo.Models { public class User { public int Id { get; set; } public string Email { get; set; } = null!; public string Password { get; set; } = null!; public string SubscriptionLevel { get; set; } = null!; // "Free", "Premium", "Pro" public DateTime? SubscriptionExpiresOn { get; set; } public string Department { get; set; } = null!; } }
Creating LoginDTO:
To generate the JWT Token, we need the user’s email and password, and for this, we need a DTO. So, inside the Models folder, create a class file named LoginDTO.cs and then copy and paste the following code.
using System.ComponentModel.DataAnnotations; namespace CustomAuthFilterDemo.Models { public class LoginDTO { [Required(ErrorMessage = "Email is required.")] [EmailAddress(ErrorMessage = "Invalid email format.")] public string Email { get; set; } = null!; [Required(ErrorMessage = "Password is required.")] [MinLength(6, ErrorMessage = "Password must be at least 6 characters long.")] public string Password { get; set; } = null!; } }
Creating UserStore:
Create a class file named UserStore.cs within the Models folder and then copy and paste the following code. This will be our in-memory data store. In real time, you need to work with a database, but for simplicity, we are hardcoding the data.
namespace CustomAuthFilterDemo.Models { public class UserStore { public static List<User> Users = new List<User> { new User {Id = 1, Email ="Alice@Example.com", Password = "alice123", SubscriptionLevel = "Pro", SubscriptionExpiresOn = DateTime.UtcNow.AddDays(5), Department = "HR" }, new User {Id = 2, Email ="Bob@Example.com", Password = "bob123", SubscriptionLevel = "Free", Department = "IT" }, new User {Id = 3, Email ="Charlie@Example.com", Password = "charlie123", SubscriptionLevel = "Premium", SubscriptionExpiresOn = DateTime.UtcNow.AddDays(-2), Department = "HR" }, new User {Id = 4, Email ="eve@Example.com", Password = "eve123", SubscriptionLevel = "Premium", SubscriptionExpiresOn = DateTime.UtcNow.AddDays(30), Department = "Sales" } }; } }
Create AuthController for Token Generation
Create a new API Empty Controller named AuthController within the Controllers folder, and then copy and paste the following code. The Login method authenticates user credentials against an in-memory store. If valid, it creates JWT claims including username, subscription, department, and expiration. The JWT token is signed and returned to the client. This token can be used for authenticated requests elsewhere in your API.
using CustomAuthFilterDemo.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; namespace CustomAuthFilterDemo.Controllers { [ApiController] [Route("api/[controller]")] public class AuthController : ControllerBase { private readonly IConfiguration _configuration; // To access configuration settings like JWT secret key // Constructor to inject IConfiguration via dependency injection public AuthController(IConfiguration configuration) { _configuration = configuration; } // POST api/auth/login endpoint - publicly accessible (no authentication required) [HttpPost("login")] [AllowAnonymous] // Allows this action to be called without authentication public IActionResult Login([FromBody] LoginDTO login) // Accepts login data from request body { // Try to find a user in your in-memory store matching email (case-insensitive) and password var user = UserStore.Users.FirstOrDefault(u => u.Email.Equals(login.Email, StringComparison.OrdinalIgnoreCase) && u.Password == login.Password); if (user == null) { // No user found with given credentials - respond with HTTP 401 Unauthorized and message return Unauthorized("Invalid username or password"); } // Create claims to embed in the JWT token for identity and authorization info var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Email), // Standard claim for username/email new Claim("SubscriptionLevel", user.SubscriptionLevel ?? "Free"), // Custom claim for subscription type (default Free) new Claim("Department", user.Department ?? "None") // Custom claim for department (default None) }; // If user has a subscription expiration date, add it as a claim in ISO 8601 string format if (user.SubscriptionExpiresOn != null) claims.Add(new Claim("SubscriptionExpiresOn", user.SubscriptionExpiresOn.Value.ToString())); // Read the JWT secret key from configuration; fallback to hardcoded key if missing (for development) var secretKey = _configuration.GetValue<string>("JwtSettings:SecretKey") ?? "d3011f8b98bbc1aa1c4ff1a7d4864fc72d9ee150bd682cf4e612d6321f57821d"; // Convert the secret key string into a byte array and create a SymmetricSecurityKey instance var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); // Create signing credentials specifying the key and the HMAC SHA256 algorithm var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); // Create the JWT token object with claims, expiration (30 mins), and signing credentials var token = new JwtSecurityToken( issuer: null, // No issuer specified (optional) audience: null, // No audience specified (optional) claims: claims, // The claims added earlier expires: DateTime.UtcNow.AddMinutes(30), // Token valid for 30 minutes from now signingCredentials: creds); // Use the signing credentials for security // Serialize the token object into a JWT compact string format var tokenString = new JwtSecurityTokenHandler().WriteToken(token); // Return the token string inside an anonymous object as JSON with HTTP 200 OK status return Ok(new { Token = tokenString }); } } }
Creating Custom Authorization Filters in ASP.NET Core Web API
There are 3 different ways to create a Custom Authorization Filter in ASP.NET Core Web API. They are as follows:
- Implement IAuthorizationFilter (Synchronous): Create a class implementing this interface and override OnAuthorization for synchronous checks.
- Implement IAsyncAuthorizationFilter (Asynchronous): For async checks like database lookups, implement this interface and override OnAuthorizationAsync.
- Inherit from AuthorizeAttribute: Create a custom attribute by extending AuthorizeAttribute. This allows you to inject extra checks alongside built-in role/policy mechanisms.
First, create a folder named Filters at the project root directory where we will create all our Custom Authorization filters.
Subscription-Based Access Control (Using IAuthorizationFilter)
In an e-commerce application, certain APIs (like exporting reports or accessing premium analytics) should only be available to users with an active “Premium” or “Pro” subscription. A custom authorization filter checks the user’s subscription level and expiration date before allowing access.
Create a class file named SubscriptionBasedAuthorizationFilter.cs within the Filters folder and then copy and paste the following code. The filter checks if the user is authenticated, verifies if the subscription level is allowed, and checks if the subscription has expired. If any check fails, it short-circuits the pipeline by setting the context.Result. The result is a JSON response with appropriate HTTP status (401 or 403) and a descriptive message. If all checks pass, the request continues normally.
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc; namespace CustomAuthFilterDemo.Filters { // Custom authorization filter that checks user subscription level and expiry public class SubscriptionBasedAuthorizationFilter : IAuthorizationFilter { // Array of allowed subscription levels passed via constructor private readonly string[] _allowedSubscriptions; // Constructor accepting allowed subscription levels as params array public SubscriptionBasedAuthorizationFilter(params string[] allowedSubscriptions) { _allowedSubscriptions = allowedSubscriptions; } // This method is called by the ASP.NET Core pipeline to authorize a request public void OnAuthorization(AuthorizationFilterContext context) { // Retrieve the current authenticated user principal from HttpContext var user = context.HttpContext.User; // Check if user is authenticated; if not, respond with 401 Unauthorized and JSON error message if (!user.Identity?.IsAuthenticated ?? true) { context.Result = CreateJsonResponse( 401, // HTTP status code "Unauthorized", // Error title "Authentication is required to access this resource." // Detailed message ); return; // Stop further processing since authorization failed } // Retrieve the "SubscriptionLevel" claim value from the user's claims var subscriptionLevel = user.FindFirst("SubscriptionLevel")?.Value; // Retrieve the "SubscriptionExpiresOn" claim value (subscription expiration date) var subExpires = user.FindFirst("SubscriptionExpiresOn")?.Value; // Check if the user's subscription level is NOT in the list of allowed subscriptions if (!_allowedSubscriptions.Contains(subscriptionLevel)) { // Return 403 Forbidden with a custom JSON error message context.Result = CreateJsonResponse( 403, "Forbidden", "Your subscription level does not allow access to this resource." ); return; // Stop further processing since authorization failed } // If the subscription expiry claim is present and can be parsed as a DateTime // and the subscription has expired (expiry date is in the past) if (subExpires != null && DateTime.TryParse(subExpires, out var exp) && exp < DateTime.UtcNow) { // Return 401 Unauthorized with a message about expired subscription context.Result = CreateJsonResponse( 401, "Unauthorized", "Your subscription has expired." ); return; // Stop further processing } // If all checks pass, the request continues to the next stage in the pipeline } // Helper method to create a standardized JSON response with status code, error, and message private JsonResult CreateJsonResponse(int statusCode, string error, string message) { // Create an anonymous object representing the JSON payload var jsonPayload = new { Status = statusCode, // HTTP status code (e.g., 401, 403) Error = error, // Error type (e.g., "Unauthorized") Message = message // Human-readable error message }; // Return a JsonResult object with the payload and the appropriate HTTP status code return new JsonResult(jsonPayload) { StatusCode = statusCode }; } } }
When to use IAuthorizationFilter in ASP.NET Core Web API?
The IAuthorizationFilter is used when you want to implement a synchronous custom authorization logic that runs before the action method executes. It allows you to write code that inspects the current HTTP context, user claims, or any other information to decide whether access should be granted or denied. This interface is suitable when your authorization check does not involve asynchronous operations, such as database or external service calls, and can be completed quickly within the same thread.
Department-Level Data Restrictions (Using IAsyncAuthorizationFilter)
In an internal HR system, only users from the HR department should be allowed to access employee salary or performance review APIs. A custom filter inspects the user’s department claim and blocks requests from users in other departments, even if they are authenticated.
Create a class file named DepartmentAuthorizationFilter.cs within the Filters folder and then copy and paste the following code. The filter checks if the user is authenticated; if not, it returns 401 with JSON. Checks if the user’s department matches the allowed department; if not, returns 403 with JSON. If all checks pass, the request can proceed. Uses an async authorization filter interface and completes the async task. Returns JSON error responses with a consistent format for 401 and 403 cases.
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc; namespace CustomAuthFilterDemo.Filters { // Custom asynchronous authorization filter that restricts access based on user's department claim public class DepartmentAuthorizationFilter : IAsyncAuthorizationFilter { // The department allowed to access the resource, passed via constructor private readonly string _allowedDepartment; // Constructor to initialize the allowed department value public DepartmentAuthorizationFilter(string allowedDepartment) { _allowedDepartment = allowedDepartment; } // This async method is called during request processing to authorize access public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { // Retrieve the current authenticated user principal from the HTTP context var user = context.HttpContext.User; // Check if the user is NOT authenticated if (!user.Identity?.IsAuthenticated ?? true) { // User is not authenticated - respond with 401 Unauthorized JSON error context.Result = CreateJsonResponse( 401, // HTTP status code "Unauthorized", // Error title "Authentication is required to access this resource." // Message ); return; // Stop further pipeline execution } // Retrieve the "Department" claim value from the user's claims var department = user.FindFirst("Department")?.Value; // Check if department claim is missing or does NOT match the allowed department (case-insensitive) if (department == null || !string.Equals(department, _allowedDepartment, StringComparison.OrdinalIgnoreCase)) { // Return 403 Forbidden with a JSON error indicating department restriction context.Result = CreateJsonResponse( 403, "Forbidden", $"Access restricted to {_allowedDepartment} department only." ); return; // Stop further pipeline execution } // Since this method is async, complete the task to satisfy interface contract await Task.CompletedTask; } // Helper method to create a JSON response with status, error, and message fields private JsonResult CreateJsonResponse(int statusCode, string error, string message) { // Create anonymous object to represent JSON payload var jsonPayload = new { Status = statusCode, // HTTP status code (401 or 403) Error = error, // Error type string Message = message // Human-readable message }; // Return the JSON response with appropriate HTTP status code return new JsonResult(jsonPayload) { StatusCode = statusCode }; } } }
When to use IAsyncAuthorizationFilter in ASP.NET Core Web API?
The IAsyncAuthorizationFilter is appropriate when your custom authorization logic needs to perform asynchronous operations such as calling a database, querying an external API, or any other I/O-bound work before granting or denying access. Since authorization can sometimes require fetching user roles, permissions, or validating membership status asynchronously, implementing this interface ensures the framework correctly awaits your authorization logic before executing the action.
Time-Based API Access (Using AuthorizeAttribute)
For a banking or support API, access should only be allowed during business hours (e.g., 9 AM to 6 PM) to prevent off-hours activity or overload. A custom authorization filter checks the server time and denies access outside the permitted window.
Create a class file named BusinessHoursAuthorizeAttribute.cs within the Filters folder and then copy and paste the following code. The filter checks if the user is authenticated; otherwise, it returns a 401 Unauthorized JSON response. Then, it checks the current local time against allowed business hours. If it is outside allowed hours, it returns a 403 Forbidden JSON response. Otherwise, it allows the request to continue normally. It returns structured JSON error responses with consistent format for client-side handling.
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc; namespace CustomAuthFilterDemo.Filters { // Custom authorization attribute to restrict API access based on business hours // Inherits from AuthorizeAttribute and implements IAuthorizationFilter for synchronous authorization logic public class BusinessHoursAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter { private readonly int _startHour; // Start of allowed access window (24-hour format) private readonly int _endHour; // End of allowed access window (24-hour format) // Constructor to initialize business hours; defaults to 9 AM to 6 PM public BusinessHoursAuthorizeAttribute(int startHour = 9, int endHour = 18) { _startHour = startHour; _endHour = endHour; } // This method is called during request processing to perform authorization check public void OnAuthorization(AuthorizationFilterContext context) { // Get the current user principal from the HTTP context var user = context.HttpContext.User; // Check if the user is NOT authenticated (null or not authenticated) if (!user.Identity?.IsAuthenticated ?? true) { // Return 401 Unauthorized with a standardized JSON error response context.Result = CreateJsonResponse( 401, // HTTP status code "Unauthorized", // Error type string "Authentication is required to access this resource." // Human-readable message ); return; // Stop further pipeline execution } // Get the current local time of day var now = DateTime.Now.TimeOfDay; // Check if current hour is outside the allowed business hours range if (now.Hours < _startHour || now.Hours >= _endHour) { // Return 403 Forbidden with JSON error indicating access restriction by time context.Result = CreateJsonResponse( 403, "Forbidden", $"API accessible only between {_startHour}:00 and {_endHour}:00 local time." ); return; // Stop further pipeline execution } // If user is authenticated and current time is within business hours, request proceeds normally } // Helper method to generate consistent JSON error responses with given status code, error, and message private JsonResult CreateJsonResponse(int statusCode, string error, string message) { // Create anonymous object to hold the response payload var jsonPayload = new { Status = statusCode, // HTTP status code e.g. 401 or 403 Error = error, // Error string like "Unauthorized" or "Forbidden" Message = message // Human-readable explanation for client }; // Return a JsonResult with the payload and specified HTTP status code return new JsonResult(jsonPayload) { StatusCode = statusCode }; } } }
When to use AuthorizeAttribute in ASP.NET Core Web API?
The AuthorizeAttribute is the standard and most common way to enforce authorization policies declaratively in ASP.NET Core. It is used when you want to apply role-based, policy-based, or claim-based authorization in a clean, reusable way by simply decorating controllers or actions with [Authorize]. You can also extend this attribute to create your own custom authorization attribute by overriding its behavior.
API Controller Applying the Custom Authorization Filters:
Create a new API Empty Controller named DemoController within the Controllers folder and then copy and paste the following code:
using Microsoft.AspNetCore.Mvc; using CustomAuthFilterDemo.Filters; namespace CustomAuthFilterDemo.Controllers { [ApiController] [Route("api/demo")] public class DemoController : ControllerBase { // Endpoint protected by subscription-based filter requiring "Premium" or "Pro" [HttpGet("premium-analytics")] // Maps HTTP GET to /api/demo/premium-analytics [TypeFilter(typeof(SubscriptionBasedAuthorizationFilter), Arguments = new object[] { new[] { "Premium", "Pro" } })] // Uses TypeFilter to apply SubscriptionBasedAuthorizationFilter with allowed subscriptions "Premium" and "Pro" public IActionResult GetPremiumAnalytics() { // Return HTTP 200 OK with JSON message confirming access to premium analytics return Ok(new { message = "Welcome to Premium Analytics." }); } // Endpoint restricted to users from the "HR" department only [HttpGet("salary-review")] // Maps HTTP GET to /api/demo/salary-review [TypeFilter(typeof(DepartmentAuthorizationFilter), Arguments = new object[] { "HR" })] // Applies DepartmentAuthorizationFilter with allowed department "HR" via TypeFilter public IActionResult GetSalaryReview() { // Return HTTP 200 OK with JSON message containing salary review data return Ok(new { message = "HR department salary review data." }); } // Endpoint accessible only during specified business hours (9 AM - 6 PM UTC) [HttpGet("support-ticket")] // Maps HTTP GET to /api/demo/support-ticket [BusinessHoursAuthorize(9, 18)] // Applies custom time-based authorization attribute restricting access to 9 AM to 6 PM public IActionResult GetSupportTicket() { // Return HTTP 200 OK with JSON message confirming access during business hours return Ok(new { message = "Support ticket API (business hours only) accessed." }); } } }
Code Explanations:
The TypeFilter attribute dynamically creates the filter instances with parameters via dependency injection. Each endpoint demonstrates a different custom authorization filter:
- Subscription level check (SubscriptionBasedAuthorizationFilter).
- Department restriction (DepartmentAuthorizationFilter).
- Business hours restriction (BusinessHoursAuthorizeAttribute).
The controller returns simple JSON messages on successful authorization.
When to Use Custom Authorization Filters in ASP.NET Core Web API
Authorization is a core security concern in any Web API. While ASP.NET Core provides built-in authorization mechanisms like the [Authorize] attribute and policy-based authorization, there are specific situations where Custom Authorization Filters become necessary or advantageous. The following are some of the scenarios where you should consider using custom authorization filters:
When Built-in Authorization Attributes Are Not Enough
The default [Authorize] attribute supports basic role-based or policy-based authorization. However, real-world business needs often require more complex authorization rules such as:
- Checking multiple dynamic conditions together (e.g., roles + subscription status + time of day).
- Applying custom business rules that don’t fit into roles or claims.
- Conditional authorization that depends on request parameters or headers.
In these cases, custom authorization filters let you implement your own logic centrally.
When Authorization Depends on External Data or Complex Logic
Sometimes you need to:
- Query a database to verify a user’s current subscription, license validity, or account status.
- Call an external API or microservice to check if the user’s session is still valid or if they have premium access.
- Enforce time-sensitive access rules (e.g., only allow access during business hours).
- Implement feature flags or A/B testing logic for access control.
A custom authorization filter lets you execute this logic at the authorization stage, preventing unauthorized users from accessing the resource before the controller runs.
When You Need Centralized Authorization Logic
Imagine multiple controllers or actions require the same custom authorization logic — for example, only users with an active subscription can access any premium API endpoint.
- Instead of repeating the same checks in every controller or action, a custom authorization filter encapsulates this logic once. You then apply it globally, on a controller, or on specific actions, achieving:
- Code Reusability and the DRY principle
- Easier maintenance and updates to authorization logic
- Cleaner controllers and action methods
When You Want to Customize Authorization Failure Responses or Behavior
The built-in [Authorize] attribute returns generic 401 (Unauthorized) or 403 (Forbidden) responses. If you want to:
- Return custom error messages explaining why access was denied.
- Log unauthorized access attempts with more details.
- Redirect users or perform special side effects on authorization failure.
- Integrate with custom logging or monitoring systems.
Custom authorization filters provide full control over the authorization pipeline, letting you customize the response or add logging before the request is blocked.
When You Need to Enforce Context-Aware Access Control
Authorization requirements might depend on the specific resource being accessed or the context of the request. For example:
- Only allow a user to access resources they own (e.g., order or profile).
- Limit access based on query parameters or headers.
- Verify IP address or geolocation restrictions.
- Check API keys or tokens beyond the default authentication.
Custom authorization filters can access the full request context (HttpContext, route data, headers, etc.) and make decisions accordingly.
Custom Authorization Filters in ASP.NET Core Web API provide a powerful, flexible way to enforce security beyond simple roles or policies. They help keep your API secure by centralizing authorization logic, supporting complex business rules, and enabling customized responses to unauthorized access attempts.
In the next article, I will discuss the Resource Filters in an ASP.NET Core Web API Application. In this article, I explain how to Create Custom Authorization Filters in ASP.NET Core Web API Applications with Examples. I hope you enjoy this article on Custom Authorization Filters in ASP.NET Core Web API.