PianoJocks avatar image
0 Votes"
PianoJocks asked PianoJocks published

Architecture Question: Best way to secure different resources behind an API exposed via AzureAD

I have an API I want to expose to consumers that is currently secured via Azure AD. I have successfully configured an auth-code OAuth2 configuration, using an Angular application as the client and a .NET 5 WebAPI. I currently have roles configured, along with policies, to ensure that all users can access a Get method, and only a few others can use the upsert/delete methods. I'll be calling them 'widgets' here.

The issue I have is that I need to limit which widgets I return, based upon their permissions. These widgets all have a factory, along with a factoryId they were created in. There are plenty of cases where I'll need admins to be able to see all the widgets from all the factories, some users only be able see the widgets from the factory they work, and then a very small set of people who see no widgets at all via the get.

I could do this by creating a permissions table, manually searching this table based upon an authenticated name, and doing a join. This seems to be an inelegant solution. I am using roles, so, in theory, I could have a role per factory, but this will be a maintenance nightmare for hundreds of factories. Not to mention, I think there's a hard limit to how many roles I can put in. Especially if there's a supervisor, who is not an admin, but has rights to 50 factories. The security token would be huge.

What would be a good way to safely, securely, and efficiently secure an API like this?

· 5
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

@PianoJocks , thank you for reaching out to us. Can you explain what policies are you using for the Azure roles and what is factory and where does it reside?

0 Votes 0 ·

I don't actually have any policies directly set yet - I am still planning this out. Also, factory is meant to be an example of where a widget is made, not a software pattern in this case! I'm trying to seek guidance as to the best practice in designing a secure API in this hypothetical model.

To better explain, the API exposes information about the output of a variety of factories, each located in different regions (say, Northeast, South, West, etc.). There are three sets of users configured in the API: the StandardUser, the RegionalManager, and GlobalAdmins. Each factory would have a RegionId, which identifies the region in the factory.

StandardUsers can access information regarding the factory they work in. So a user who works in Factory_NY should be able to access a GET method to retrieve information about Factory_NY. If it they try to access the GET method for specific information about any other factory (such as Factory_NY), they should get a 403 response code.

RegionalManagers should be able to access any Factory which is in their regional purview. So, the user who is a RegionalManager for the Northeast should be able to successfully retrieve information from any plant in the Northeast (such as Factory_NY, Factory_NJ, and so on). However, if they were to call the GET method for information on Factory_CA, it should return a 403 response code.

GlobalAdmins should have access to all Factories, regardless of regions.

0 Votes 0 ·

If it they try to access the GET method for specific information about any other factory (such as Factory_NY), they should get a 403 response code.

Mistake here it should read: If it they try to access the GET method for specific information about any other factory (such as Factory_NJ), they should get a 403 response code.

0 Votes 0 ·

I would have imagine that we should have three roles, one for each user type, along with a default ASP.NET Core policy of requiring one of the above roles. However, it's the selectivity that is the issue for me. The RegionalManager for Northeast would be in the same role as the RegionalManager for the South, but they should not have the same results from a GET method call: they're in different regions. I'll also likely need to consider a case where there is a StandardUser who needs access to multiple factories, but not necessarily in the same region.

If I create a role for every plant, it would get very unwieldy. For example, if this company has 500 plants, and a standard user needs access to 60 of them, their could be 60 roles put into the claims principal. This doesn't seem right to me.

One possible solution is that I use the default policy of ensuring one of the roles, and in the C# code of the API controller method, manually look up the user's regional permissions and alter my results accordingly. This works, but it seems like I'm splitting my authorization logic: the auth policy would check for role membership, but then the other authorization code is in the controller.

Another possibility is to write a custom IAuthorizationService that also looks for the presence of a HttpHeader that includes the Factory name and handles all of the Authorization there.

Is there a better way of handling different authorization permissions for hundreds of different factories?

(Hope this was more clear)

0 Votes 0 ·

@PianoJocks , yes, definitely it is much more clear now. Thanks for the explanation. I think the best is to combine claim based and role based authorization. For example, if value of Attribute1 is X and Role is Y, grant access to WidgetZ. Let me know if this answers your query.

0 Votes 0 ·

1 Answer

PianoJocks avatar image
0 Votes"
PianoJocks answered PianoJocks published

I'm not quite certain I follow. I understand you're saying i should use a combination of Claims and Role-based authorization, but where would each part be checked? I'll try to elaborate.

Let's use the example I have described above. As I am building a WebAPI, with the expectation that an authenticated user is attempting to obtain a list of Widgets that they have to work with for some reason.

If this was a simple request for a single user, I could make a WidgetController.cs class, with an HttpGet attribute and check for a Role that permits access to Widgets.

But, in my case, the list of Widgets needed is based upon authentication:

  • A StandardUser would get all of the Widgets from their factory

  • A RegionalManager would get all of the Widgets from their region (many factories)

  • A GlobalAdmin would get all of the Widgets from the whole company.

  • There are edge cases where a specific user would need access to factories not normally associated with their role

Is the suggestion having a Role for each factory? If so, where should the AuthZ check be? If I make middleware, we somehow need to know the factoryId related to the request. Putting a factory in a header seems kludgy, so we'd need to possibly be consistent with a Query String variable and grab that value of that parameter in the middleware?

You mention claims - how do you envision a claim appearing for each type of user? And where would these claims get populated? If it's Azure AD, we might end up having hundreds of factories, which would mean 100s of claims.

Can you elaborate a bit more, please? Thx

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.