ASP.NET Core has seen an overhaul to its authorization system in the form of authorization policies. As a developer, you can add several policies that can check claims, require an authenticated user, and even handle a custom requirement with any code you can write. It’s a powerful module system that allows for incredible flexibility when building your authorization system. However, with tremendous flexibility comes the inevitable complexity of finding and implementing the right part to change the system’s behavior. This post will explore how to change the behavior and the ultimate result of a failed authorization policy.
Adding a Custom Authorization Policy
If you want to use your policy across all the frameworks inside ASP.NET Core (Minimal APIs, MVC, and Razor Pages), you’ll want to define your policies within your Program file. Policies are straightforward to implement, as there’s a list of requirements a user must meet before authorizing a request. First, let’s look at a specific policy.
The woopsy policy requires that the user be an authenticated user and that they meet the FaileRequirement. Requirements have a matching AuthorizationHandler which uses the requirement instance to identify an endpoint and potentially accept any additional metadata. So how are the pair of FailRequirement and FailHandler implemented?
In this case, the handler immediately fails the request by calling context.Fail while setting an AuthorizationFailureReason to be used later in the pipeline. As mentioned previously, the FailRequirement class is a marker class, helping us identify the AuthorizationHandler that ASP.NET Core should use to meet the requirement.
Let’s add the policy to a Minimal API endpoint (.NET 7+).
Cool! Now we’re ready to fail. What about handling the authorization failure?
Handling the AuthorizationFailureReason
In most cases, ASP.NET Core will handle failed authorizations by either returning a 403 Forbidden status code or trying to work with your authentication and authorization pipeline defaults. That’s perfectly fine, but sometimes you want to handle failed policies differently. Some examples include:
- Development-time diagnostic pages to help debug policy issues.
- Concealing administrative endpoints with
404 Not Foundfor extra security rather than returning a403 Forbiddenstatus code. - Redirect users to support pages when failed policies have more to do with business policy than technical failure (payment overdue).
The list of possibilities is endless and really up to your imagination.
To handle failed authorizations, we need to register a new IAuthorizationMiddlewareResultHandler instance. This new handler will accept several data points and allow you to change the response flow.
For our implementation, we want to return an HTML page and stop the request processing when our FailRequirement policy fails.
You have access to the HttpContext, so you could potentially do anything with the current request/response. In our case, we only want to change the default behavior when ASP.NET Core handles the FailRequirment. In all other cases, we want ASP.NET Core to use the default handler to complete the user’s request.
I’ve included the complete ASP.NET Core Minimal API example below to see it all in action.
Conclusion
There you have it. I hope you’ve learned something new about the authorization pipeline in ASP.NET Core and how you may modify it to meet your use case. Let me know if you have any questions or thoughts by following me on Twitter at @buhakmeh. As always, thanks for reading.
Photo by 