The document discusses authorization in .NET Core, including:
- Custom authorization policies are made up of requirements and handlers and allow expressing authorization logic in code.
- Requirements define the criteria checked by handlers, while handlers implement the logic to evaluate requirements against user claims.
- Multiple handlers can be used for a single requirement to provide an "OR" evaluation of authorization.
- Policies are applied to controllers and actions using the Authorize attribute and requirement names defined in configuration.
- The authorization context passed to handlers allows accessing user claims and succeeding/failing authorization for a requirement.
social pharmacy d-pharm 1st year by Pragati K. Mahajan
Authorization iii
1. Authorization
Part III
Custom Policy-Based Authorization
Both the role authorization and claims authorization make use of a need, a handler for the requirement
and a per-configured policy.
They allow you to express authorization access in code, allowing a richer, reusable, and easily testable
authorization structure.
An authorization policy is made up of one or more needs and registered at application startup as part of
the Authorization service configuration, in ConfigureServices in the Startup.cs file system.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
2. {
options.AddPolicy("Over21",
policy => policy.Requirements.Add(new MinimumAgeRequirement(21)));
}
});
Here you see an “Over21” policy is created with a single requirement of a minimum age, which is
passed as a requirement.
Policies are applied using the Authorize feature by specifying the policy name, e.g.
[Authorize(Policy="Over21")]
public class AlcoholPurchaseRequirementsController : Controller
{
public ActionResult Login()
{
}
public ActionResult Logout()
{
}
}
Requirements
An authorization requirement is a sum of data parameters that a policy can use to evaluate the current
user principal. In the Minimum Age policy, the requirement is of a single parameter. A requirement
must implement IAuthorizationRequirement. This is a void, marker interface. The criteria of minimum
age requirement might be implemented as follows;
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public MinimumAgeRequirement(int age)
{
MinimumAge = age;
}
3. protected int MinimumAge { get; set; }
}
A requirement doesn’t need to have data or properties.
Authorization Handlers
This is responsible for the assay of any properties of requirement. The authorization handler must
assay them against a provided AuthorizationContext to finalize if authorization is allowed. A
requirement can have many handlers. Handlers must inherit AuthorizationHandler<T> where the
alphabet T is the need it handles.
The minimum age handler might look like this:
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationContext context,
MinimumAgeRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
c.Issuer == "http://contoso.com"))
{
return Task.FromResult(0);
}
var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(
c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "http://contoso.com").Value);
int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
{
calculatedAge--;
}
4. if (calculatedAge >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
return Task.FromResult(0);
}
}
In the code above let's first see if the current user principal has a DOB claim which has been issued by
an Issuer we could know and trust. If the claim is missing we can’t authorize so we return. If we have a
claim, we find out how old the user is, and if they meet the minimum age criteria then the authorization
is successful. Once it is a success we call context.Succeed() passing in the requirement.
Handlers must be listed in the services collection during configuration, for example;
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
options.AddPolicy("Over21",
policy => policy.Requirements.Add(new MinimumAgeRequirement(21)));
});
services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}
Each handler is added to the services collection by using
services.AddSingleton<IAuthorizationHandler, YourHandlerClass>(); passing in your handler class.
You can see in the handler example that the Handle() method has no return value, so how do we
indicate success and failure.
A handler depicts success by calling context.Succeed passing the need that has been successfully
assayed.
5. To guarantee failure even if other handlers for a requirement succeed, refer context.Fail.
Why would I want many handlers for a requirement?
In cases where you want the assay on an OR basis, you use multiple handlers for only one requirement.
E.g. , Microsoft has doors which open with key cards only. If you leave your key card at home the
responsible authority prints a temporary sticker and opens the door for you. In this case, you have a
single requirement, EnterBuilding, but many handlers, each one examining a single requirement.
public class EnterBuildingRequirement : IAuthorizationRequirement
{
}
public class BadgeEntryHandler : AuthorizationHandler<EnterBuildingRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationContext context,
EnterBuildingRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == ClaimTypes.BadgeId &&
c.Issuer == "http://microsoftsecurity"))
{
context.Succeed(requirement);
return Task.FromResult(0);
}
}
}
public class HasTemporaryStickerHandler : AuthorizationHandler<EnterBuildingRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationContext context,
EnterBuildingRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == ClaimTypes.TemporaryBadgeId &&
6. c.Issuer == "https://microsoftsecurity"))
{
// We'd also check the expiry date on the sticker.
context.Succeed(requirement);
return Task.FromResult(0);
}
}
}
Now, if both handlers are registered when a policy assays the EnterBuildingRequirement if either
handler does succeed the policy evaluation will succeed.
Using a function to fulfill a policy
There may be occasions where satisfying a policy is simple to express in code. It is possible to simply
supply a Func<AuthorizationHandlerContext, bool> when configuring the policy with the
RequireAssertion policy builder.
E.g. the previous BadgeEntryHandler could be again written as follows;
services.AddAuthorization(options =>
{
options.AddPolicy("BadgeEntry",
policy => policy.RequireAssertion(context =>
context.User.HasClaim(c =>
(c.Type == ClaimTypes.BadgeId ||
c.Type == ClaimTypes.TemporaryBadgeId)
&& c.Issuer == "https://microsoftsecurity"));
}));
}
}
Accessing MVC Request the context In Handlers
You must implement the Handle method which has two contexts, an AuthorizationContext and the
Requirement you are handling. Frameworks such as MVC or Jabber are free to join any object to the
Resource property on the AuthorizationContext to pass through extra information.
7. If you want to improve your skill set in ASP.Net and optimize yourself in .NET training, then our CRB
Tech Solutions would be of great support for you. Join us with our scientifically designed program of
ASP.Net course.
Stay connected to CRB Tech reviews for more technical optimization and other resources.