Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Implementing Authorization for Applications and APIs

Implementing Authorization for Applications and APIs

NDC Oslo 2017

75681814fbbb90c9224ea5ed0f8324ee?s=128

Dominick Baier

June 20, 2017
Tweet

More Decks by Dominick Baier

Other Decks in Programming

Transcript

  1. Implementing Authorization for Applications & APIs Dominick Baier & Brock

    Allen https://identityserver.io identity@leastprivilege.com @leastprivilege / @brocklallen
  2. 2 @leastprivilege / @brocklallen Authorization is hard! • Many approaches

    – roles, permissions, resource-based, ACLs… (and permutations) – queries vs commands • No standard solution – often very application specific – blurry line between authorization and business rules – XACML good example of failed attempt to standardize
  3. 3 @leastprivilege / @brocklallen Modern Applications Browser Native App Server

    App "Thing" Web App Web API Web API Web API Identity Provider
  4. 4 @leastprivilege / @brocklallen Identity != Permissions

  5. 5 @leastprivilege / @brocklallen Overloaded Security Token { "iss": "https://idsrv4",

    "exp": 1340819380, "aud": [ "api1", "api2"], "amr": [ "password" ], "auth_time": 12340819300 "sub": "182jmm199", "name": "Doug Ross", "role": [ "Approver", "Doctor" ], "permission": [ "DeleteData", "ManageCustomers", "ChangeTreatmentPlan" ] } authentication metadata identity authorization data roles
  6. 6 @leastprivilege / @brocklallen Token Usage Patient Data API Oncology

    API Cardiac API
  7. 7 @leastprivilege / @brocklallen Permissions and Tokens • Separation of

    concerns – authentication vs authorization – identity system does not have intimate knowledge of application specific authorization rules • "do authorization as close as possible to the resource you are trying to protect" • Tokens can be re-used at several places – claims might have different meaning for each consumer – token & claim bloat • Permissions might change – only way to update the data would be to get a new token
  8. 8 @leastprivilege / @brocklallen Identity + Permissions == Authorization

  9. 9 @leastprivilege / @brocklallen Modern Applications Browser Native App Server

    App "Thing" Web App Web API Web API Web API Identity Provider Authorization Provider
  10. 10 @leastprivilege / @brocklallen Patient API Oncology API Cardiac API

    Identity Provider Authorization Provider authentication + token request get client specific permissions call APIs get API specific permissions Admin UI
  11. 11 @leastprivilege / @brocklallen Global roles (static / dynamic) Management

    API Client API App specific roles (static / dynamic) Roles to permission mappings resources & rules (future) Applications Admin UI Client Library Our Vision { "sub": "jd9j91199j1", "role": "Doctor", "contractor": "true" } Authorization Provider
  12. 12 @leastprivilege / @brocklallen Client Integration Strategies • Provide a

    client library and call the authorization provider manually – client library takes care of caching & refreshing • Augment the ClaimsPrincipal – e.g. using claims transformation in pipeline • Use a specialized authorization API – e.g. ASP.NET Core Authorization
  13. 13 @leastprivilege / @brocklallen Client Library public class TreatmentController :

    Controller { private readonly AuthorizationProviderClient _client; public TreatmentController(AuthorizationProviderClient client) { _client = client; } public async Task<IActionResult> Update(TreatmentUpdateModel model) { var (roles, permissions) = await _client.GetAuthorizationAsync(User); // or var allowed = await _client.IsInRoleAsync(User, "Doctor"); // or allowed = await _client.HasPermissionAsync(User, "PrescribeMedication"); } }
  14. 14 @leastprivilege / @brocklallen Augment the ClaimsPrincipal • Inject roles

    and permissions into current principal – backwards compat with existing libraries [Authorize(Roles = "Doctor")] public async Task<IActionResult> Update(TreatmentUpdateModel model) { ... }
  15. 15 @leastprivilege / @brocklallen ASP.NET Core Authorization • Very flexible

    authorization library – created for ASP.NET Core, but has been back-ported by the community* • Introduces a policy-based framework – decoupling authorization logic from business code – extensible – supports resource-based authorization * https://github.com/DavidParks8/Owin-Authorization
  16. 16 @leastprivilege / @brocklallen Authorization Policies services.AddAuthorization(options => { options.AddPolicy("PrescribeMedication",

    policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("permission", "PrescribeMedication"); }); }); [Authorize("PrescribeMedication")] public IActionResult Update() { // stuff } Startup Controller
  17. 17 @leastprivilege / @brocklallen Programmatically using policies public class TreatmentController

    : Controller { private readonly IAuthorizationService _authz; public TreatmentController(IAuthorizationService authz) { _authz = authz; } public async Task<IActionResult> Update() { var allowed = await _authz.AuthorizeAsync(User, "PrescribeMedication"); if (!allowed) return Challenge(); return View(); } }
  18. 18 @leastprivilege / @brocklallen …or from a View @using Microsoft.AspNetCore.Authorization

    @inject IAuthorizationService _authz @if (await _authz.AuthorizeAsync(User, "PrescribeMedication")) { <div> <a href="/treatment/update">Update</a> </div> }
  19. 19 @leastprivilege / @brocklallen Policy Provider • Extensibility point that

    allows creating policies on the fly – no need to create explicit "permission policies" anymore // this policy is not statically defined // but gets created dynamically // check for permission claim [Authorize("PrescribeMedication")] public IActionResult Update() { // stuff } Controller
  20. 20 @leastprivilege / @brocklallen Example Policy Provider public class AuthorizationPolicyProvider

    : DefaultAuthorizationPolicyProvider { public async override Task<AuthorizationPolicy> GetPolicyAsync(string policyName) { // check static policies first var policy = await base.GetPolicyAsync(policyName); if (policy != null) return policy; if (await _client.HasPermissionAsync(_contextAccessor.HttpContext.User, policyName)) { return Allowed; } return Denied; } }
  21. 21 @leastprivilege / @brocklallen Custom Requirements public class MedicationRequirement :

    IAuthorizationRequirement { public string MedicationName { get; set; } public int Amount { get; set; } }
  22. 22 @leastprivilege / @brocklallen Requirement Handler public class MedicationRequirementHandler :

    AuthorizationHandler<MedicationRequirement> { private readonly AuthorizationProviderClient _client; public MedicationRequirementHandler(AuthorizationProviderClient client) { _client = client; } protected override async Task HandleRequirementAsync( AuthorizationHandlerContext context, MedicationRequirement requirement) { var user = context.User; var allowed = false; if (await _client.HasPermissionAsync(user, "PrescribeMedication")) { if (requirement.Amount < 10) allowed = true; else allowed = await _client.IsInRoleAsync(user, "Doctor"); if (allowed || requirement.MedicationName == "placebo") { context.Succeed(requirement); } } } }
  23. 23 @leastprivilege / @brocklallen Using a custom Requirement public async

    Task<IActionResult> Prescribe(int amount) { var meds = new MedicationRequirement { MedicationName = "aspirin", Amount = amount }; var allowed = await _authz.AuthorizeAsync(User, meds); if (!allowed) return Challenge(); return View("Confirm"); }
  24. 24 @leastprivilege / @brocklallen Resource-based Authorization Subject Object Operation -

    client ID - subject ID - scopes - more claims + DI - read - write - send via email - ... - ID - owner - more properties + DI
  25. 25 @leastprivilege / @brocklallen Example: Patient Resource public class Patient

    { public ICollection<string> Allergies { get; set; } }
  26. 26 @leastprivilege / @brocklallen Patient Handler public class PatientHandler :

    AuthorizationHandler<PrescribeMedicationOperation, Patient> { private readonly AuthorizationProviderClient _client; public PatientHandler(AuthorizationProviderClient client) { _client = client; } protected override async Task HandleRequirementAsync( AuthorizationHandlerContext context, PrescribeMedicationOperation medication, Patient patient) { // medication requirement logic if (!patient.Allergies.Contains(medication.MedicationName)) { allowed = false; } if (allowed) context.Succeed(medication); } }
  27. 27 @leastprivilege / @brocklallen Invoking the Patient Handler public async

    Task<IActionResult> PrescribeMed(int amount, string medication) { var operation = new PrescribeMedicationOperation { Amount = amount, MedicationName = medication }; var patient = new Patient { Allergies = { "aspirin" } }; var allowed = await _authz.AuthorizeAsync(User, patient, operation); if (!allowed) return Challenge(); return View("Confirmation"); }
  28. 28 @leastprivilege / @brocklallen Conclusion • Different levels of authorization

    lend to different abstractions Application/API application access feature feature feature access application logic ?