Download - Authorization in ASP.NET Core... · Authorization in ASP.NET Core •Complete re-write •support for unauthorized vs forbidden •better separation of business code and authorization

Transcript

Authorization in ASP.NET Core

Brock Allen

[email protected]

http://brockallen.com

@BrockLAllen

Authorization in ASP.NET Core

• Complete re-write• support for unauthorized vs forbidden

• better separation of business code and authorization logic

• re-usable policies

• resource/action based authorization

• DI enabled

[Authorize]

• [Authorize] attribute still supported• [AllowAnonymous] also still

supported

• Custom implementations no longer supported• More on this in a bit…

[Authorize]public class HomeController : Controller{

[AllowAnonymous]public IActionResult Index(){

return View();}

[Authorize(Roles = "Sales")]public IActionResult About(){

return View(User);}

}

Unauthorized vs. Forbidden

Unauthorized : User is anonymous

302 to LoginPath

Forbidden : User is authenticated

302 to AccessDeniedPath

public void Configure(IApplicationBuilder app){

app.UseCookieAuthentication(new CookieAuthenticationOptions{

AuthenticationScheme = "Cookies",AutomaticAuthenticate = true,AutomaticChallenge = true,LoginPath = new PathString("/Account/Login"),AccessDeniedPath = new PathString("/Home/Forbidden"),

});}

Role-based authorization in ASP.NET

• Roles were promoted as the primary approach to authorization• Still supported in ASP.NET Core

• High coupling to roles is an anti-pattern

public class CustomerController : Controller{

[Authorize(Roles = "Sales")]public IActionResult Add(string name){

return View();}

}

Policy-based authorization

• Framework to promote better authorization style• Decoupled from other application logic

• Reusable from many locations in application code

public class CustomerController : Controller{

[Authorize(Policy = "ManageCustomers")]public IActionResult Add(string name){

return View();}

}

Defining a policy

• List of requirements• All must pass

• Registered in DI• Can be encapsulated

in separate class

public void ConfigureServices(IServiceCollection services){

services.AddAuthorization(options =>{

options.AddPolicy("ManageCustomers", policy =>{

policy.RequireAuthenticatedUser().RequireRole("Sales").RequireClaim("level", "senior").RequireAssertion(ctx =>{

return ctx.User.HasClaim("location", "USA") || ctx.User.IsInRole("Admin");

});});

});}

Programmatically using policies

• IAuthorizationService provides access to authorization framework

public class CustomerController : Controller{

private readonly IAuthorizationService _authz;

public CustomerController(IAuthorizationService authz){

_authz = authz;}

public async Task<IActionResult> Add(string name){

var allowed = await _authz.AuthorizeAsync(User, "ManageCustomers");if (!allowed) return Challenge();

return View();}

}

Razor also supports injection

@using Microsoft.AspNetCore.Authorization@inject IAuthorizationService _authz

@if (await _authz.AuthorizeAsync(User, "ManageCustomers")){

<div><a href="/Customers/ViewAll">Customer List</a>

</div>}

Custom requirements

• Requirements can be custom and/or dynamic

public class LocationRequirement : IAuthorizationRequirement{

public string RequiredLocation { get; set; }}

public async Task<IActionResult> ViewCustomers(string country = "USA"){

var locationRequirement = new LocationRequirement { RequiredLocation = country };var allowed = await _authz.AuthorizeAsync(User, null, locationRequirement);if (!allowed) return Challenge();

return View();}

AuthorizationHandlers implement logic

public class LocationRequirementHandler : AuthorizationHandler<LocationRequirement>{

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, LocationRequirement requirement)

{var userLocation = context.User.FindFirst("location")?.Value;if (userLocation == requirement.RequiredLocation){

context.Succeed(requirement);}

return Task.FromResult(0);}

}

services.AddTransient<IAuthorizationHandler, LocationRequirementHandler>();

Resource-based Authorization

Subject ObjectOperation

- client ID- subject ID- scopes

- more claims

+ DI

- read- write- send via email- ...

- ID- owner

- more properties

+ DI

Define resource and operations

• Resource is just a class

• Operations are named OperationAuthorizationRequirement

public class Customer{

public int Id { get; set; }public string Name { get; set; }public string SalesRepId { get; set; }

public static OperationAuthorizationRequirement Edit= new OperationAuthorizationRequirement { Name = "Edit" };

public static OperationAuthorizationRequirement Delete= new OperationAuthorizationRequirement { Name = "Delete" };

}

Implement resource-based logic & add to DIpublic class CustomerHandler : AuthorizationHandler<OperationAuthorizationRequirement, Customer>{

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, OperationAuthorizationRequirement operation, Customer customer)

{if (operation == Customer.Edit){

var userId = context.User.FindFirst("userId").Value;if (userId == customer.SalesRepId){

context.Succeed(operation);}

}

return Task.FromResult(0);}

}

services.AddTransient<IAuthorizationHandler, CustomerHandler>();

Invoke resource-based authorizationpublic class CustomerController : Controller{

private readonly IAuthorizationService _authz;

public CustomerController(IAuthorizationService authz){

_authz = authz;}

[HttpPost]public async Task<IActionResult> Update(Customer customer){

var allowed = await _authz.AuthorizeAsync(User, customer, Customer.Edit);if (!allowed) return Challenge();

return View();}

}

Summary

• ASP.NET authorization framework is policy based framework

• Policies decouple application logic from authorization logic

• Resource based authorization is a style of policy authorization