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 Found
for extra security rather than returning a403 Forbidden
status 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.