This article shows how to implement a custom ASP.NET Core policy using the AuthorizationHandler class. The handler validates, that the identity from the HttpContext has the authorization to update the object in the database.
Code: https://github.com/damienbod/AspNetCoreAngularSignalRSecurity
Scenerio
In the example, each admin user of the client application, can create DataEventRecord entities which can only be accessed by the corresponding identity. If a different identity with a different user sends a PUT request to update the object, a 401 response is returned. Because the Username from the identity is saved in the database for each entity, the custom policy can validate the identity and the entity to be updated.
Creating the Requirement for the Policy
A simple requirement is created for the policy implementation. The AuthorizationHandler implementation requires this and the requirement class is also used to add the policy to the application.
using Microsoft.AspNetCore.Authorization; namespace ApiServer.Policies { public class CorrectUserRequirement : IAuthorizationRequirement { public class CorrectUserRequirement : IAuthorizationRequirement{} } }
Creating the custom Handler for the Policy
If a method is called, which is protected by the CorrectUserHandler, the HandleRequirementAsync is executed. In this method, the id of the object to be updated is extracted from the url path. The id is then used to select the Username from the database, which is then compared to the Username from the HttpContext identity name. If the values are not equal, no success message is returned.
using ApiServer.Repositories; using Microsoft.AspNetCore.Authorization; using System; using System.Threading.Tasks; namespace ApiServer.Policies { public class CorrectUserHandler : AuthorizationHandler<CorrectUserRequirement> { private readonly IDataEventRecordRepository _dataEventRecordRepository; public CorrectUserHandler(IDataEventRecordRepository dataEventRecordRepository) { _dataEventRecordRepository = dataEventRecordRepository; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CorrectUserRequirement requirement) { if (context == null) throw new ArgumentNullException(nameof(context)); if (requirement == null) throw new ArgumentNullException(nameof(requirement)); var authFilterCtx = (Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext)context.Resource; var httpContext = authFilterCtx.HttpContext; var pathData = httpContext.Request.Path.Value.Split("/"); long id = long.Parse(pathData[pathData.Length -1]); var username = _dataEventRecordRepository.GetUsername(id); if (username == httpContext.User.Identity.Name) { context.Succeed(requirement); } return Task.CompletedTask; } } }
Adding the policy to the application
The custom policy is added to the ASP.NET Core application using the AddAuthorization extension using the requirement.
services.AddAuthorization(options => { ... options.AddPolicy("correctUser", policyCorrectUser => { policyCorrectUser.Requirements.Add(new CorrectUserRequirement()); }); });
Using the policy in the ASP.NET Core controller
The PUT method uses the correctUser policy to authorize the request.
[Authorize("dataEventRecordsAdmin")] [Authorize("correctUser")] [HttpPut("{id}")] public IActionResult Put(long id, [FromBody]DataEventRecordDto dataEventRecordDto) { _dataEventRecordRepository.Put(id, dataEventRecordDto); return NoContent(); }
If a user logs in, and tries to update an entity belonging to a different user, the request is rejected.
Links:
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies
