Image by @claybanks
Authorization Policy
Authorizing the resource access is essential part of any API. The .NET provides you a perfect mental model which is easier to reason about. It has this flow:
- What is the name of your policy as string.
- What requirement the user must satisfy to qualify which implements the
IAuthorizationRequirement
interface. - What is your handler responsible to evaluate, which inherits the
AuthorizationHandler<UniqueIdHeaderRequirement>
and register it.
Then Authorize
attribute allows you to set a policy name when used on controller or action method.
But if you are fan of Minimal API then fluent style is the way to go using RequireAuthorization
.
How to define Authorization Policy?
The AuthorizationOptions
allows you to add policies with requirements using the AddAuthorization
.
Once the policy is defined and configured, it can be applied to the end point referred as resource.
Authorize using a Policy to verify that http header exists
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
services.AddSingleton<IAuthorizationHandler, UniqueIdHeaderRequirement.UniqueIdHeaderRequirementAuthorizationHandler>();
// Required otherwise failure of auth handler will throw exception
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
services.AddAuthorization(options =>
{
options.InvokeHandlersAfterFailure = false;
options.AddPolicy("VerifyXUniqueIdHeader",
policyBuilder => {
policyBuilder.AddRequirements(new UniqueIdHeaderRequirement());
});
});
services.AddRouting();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(routeBuilder =>
{
//Fluent style
routeBuilder.MapGet("/", () => Results.Ok()).RequireAuthorization("VerifyXUniqueIdHeader");
//Attribute style - Same be done MVC controller or action
routeBuilder.MapGet("/hello", [Authorize(Policy = "VerifyXUniqueIdHeader")]() => Results.Ok());
});
app.Run();
internal class UniqueIdHeaderRequirement : IAuthorizationRequirement //IAuthorizationRequirement is a marker interface
{
// Handler gets called when the user is trying to access the resource.
internal class UniqueIdHeaderRequirementAuthorizationHandler : AuthorizationHandler<UniqueIdHeaderRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
UniqueIdHeaderRequirement requirement)
{
//for mvc filter context cast it to - AuthorizationFilterContext
if (context.Resource is HttpContext httpContext && !httpContext.Request.Headers.ContainsKey("x-unique-id"))
{
context.Fail();
return Task.CompletedTask;
}
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
Feedback
I would love to hear your feedback, feel free to share it on Twitter.