How to handle authorization with Breeze JS?
Asked Answered
W

3

3

Currently my app looks at router parameter and logged in user (Principal.Identity) to authorize access to certain resources (e.g: Add student to your class [identity + class id]). However, If I'm not wrong, breeze js support just one bulk save. It seems to be that I will have to open each and every data and run through the validation/authorization. That is fine,

but what I may lose is nice separation of cross cutting concern out side my business logic (as a message handler) (finding what roles user has on the class) and nice Authroize annotation feature (just say what roles are needed). So do I have to trade off or is there better programming model which Breeze JS might suggest?

Update: My question is more on how to separate the authorization (find assigned roles in message handler + verify if required roles are present by adding authorize attribute to controller methods) logic from business or data access logic. Without breeze, I will inspect the incoming message and its route parameter to fetch all its roles then in my put/post/delete methods I would annotate with required roles. I cannot use this technique with breeze (its not breeze's limitation, its trade off when you go for bulk save). So wanted to know if there is any programming model or design pattern already used by breeze guys. There is something on breeze's samples which is overriding context and using repository pattern, will follow that for now.

Weldonwelfare answered 24/11, 2013 at 19:47 Comment(0)
X
1

Breeze can have as many 'save' endpoints as you want. For example, a hypothetical server implementation might be

[BreezeController]
public class MyController : ApiController {

  [HttpPost]
  [Authorize(...)]
  public SaveResult SaveCustomersAndOrders(JObject saveBundle) {
    // CheckCustomersAndOrders would be a custom method that validates your data 
    ContextProvider.BeforeSaveEntitiesDelegate = CheckCustomerAndOrders;
    return ContextProvider.SaveChanges(saveBundle);
  }

  [HttpPost]
  [Authorize]
  public SaveResult SaveSuppliersAndProducts(JObject saveBundle) {
     ...
  }

You would call these endpoints like this

var so = new SaveOptions({ resourceName: "SaveWithFreight2", tag: "freight update" });

   myEntityManager.saveChanges(customerAndOrderEntities, { 
     resourceName: "SaveCustomersAndOrder" }
   ).then(...)

or

   myEntityManager.saveChanges(supplierAndProductEntities, { 
     resourceName: "SaveSuppliersAndProducts" }
   ).then(...)

Authorization is mediated via the [Authorize] attribute on each of the [HttpPost] methods. You can read more about the [Authorize] attribute here: http://sixgun.wordpress.com/2012/02/29/asp-net-web-api-basic-authentication/

Xylon answered 26/11, 2013 at 1:1 Comment(1)
Thx for the detailed answer. In my case I need a value in the URL for Authorization message handler to pull all the available roles, the role fetching also depends on operations, if update/delete/add, as I have roles for each and the client may have one role while not other (can update but not add/delete). As I understand from your answer, I may have to deal with getting the roles based on EntityState and verify them manually without using [Authorize] attribute.Weldonwelfare
L
1

The proper way to do this IMHO is to separate the endpoint authorization and the database actions authorization.

First, create an entity that manages the grands per controller/method and role. For each method you have a value allowed - not allowed for the specific role. You create a special attribute (subclass of Authorize) that you apply to your controllers (breeze or plain web api) that reads the data and decides whether the specific endpoint can be called for the user/role. Otherwise it throws the Unauthorized exception.

On the breeze side (client) you extend the default adapter settings with a method that adds the authentication headers from identity that you received at login, something like this :

var origAjaxCtor = breeze.config.getAdapterInstance('ajax');

$.extend(true, origAjaxCtor.defaultSettings, Security.getAuthenticationHeaders());

On the server, add a second entity that manages the authorization for the CRUD operations. You need a table like (EntityName, AllowInsert, AllowUpdate, AllowDelete). Add a BeforeSave event on the Context Manager or on the ORM (EF or something else) that loops all entities and applies the policy specified on the table above. This way you have a clear separation of the endpoint logic from the backend CRUD logic.

In all cases the authorization logic should first be implemented server side and if needed should be pushed to the clients.

The way breeze is implemented and with the above design you should not need more than 1 save endpoint.

Hope it helps.

Lyons answered 30/5, 2016 at 11:11 Comment(3)
I really want to understand your answer because I'm trying to implement security in our breeze-enabled app. And, I think I get the part about 'on the server'. That's about securing save access, and I implemented something like what you describe. But, I don't get the preceding parts. I think you're describing how to limit access to an endpoint based on role. But the problem is that an unrestricted breeze query endpoint could be passed a odata query (from a malicious client) that requests (related) restricted data. How do I prevent that?Tiphany
All endpoints should be restricted. The authorize attribute should be applied to the base controller (I strongly advice that you have a base class for that). This way all endpoints will be secured. I will post some code later today to show how it can be implementedLyons
We do use the authorize attribute on the controller class. My use of 'restricted' and 'unrestricted' is WRT role-based access.Tiphany
H
0

However, If I'm not wrong, breeze js support just one bulk save.

That is entirely wrong. You have free reign to create your own save methods. Read the docs, it's all there.

Hilel answered 24/11, 2013 at 22:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.