I'm building out a REST API with .NET Core Web API.
My controllers simply forward requests to the service layer and return the result
[HttpPost(nameof(Create))]
public async Task<Response<ProviderDTO>> Create([FromBody] ProviderDTO provider)
=> await providerService.CreateAsync(provider);
I'm at the point in the system now where I need to start implementing authorization.
.NET Core has a lot of options to implement authorization, but the documentation predominantly discusses these approaches in the context of the Web layer (where the controllers obviously live).
My instincts are telling me that I need to be implementing the authorization within the service layer itself, rather than placing this authorization on the web layer.
Some of my reasoning includes:
- Service layer could get called by something other than a controller (for example, services calling other services, which wouldn't be concerned at that point with authorization).
- Service authorization can be unit tested directly, rather than having to rely on integration tests being written for each "layer" that goes in front of the services.
- Saves multiple calls to the database -- if I need to authorize a document in an authorization requirement if it passed I'd then have to pull the same document out later in the service.
Question One
Would it be a sensible approach to inject IPrincipal
and IAuthorizationService
into my services and handle authorization directly in there? Then the web layer would purely just check the user is logged in, and perhaps some simpler policy based attributes (i.e. this controller only allows staff policy for example)
Question Two
Does anyone have any resources they could link me through to (I did research, but there's not much out there on this)
PS: Regarding rejecting requests in the service layer, I have an exception handling middleware that converts custom exceptions to HTTP responses. Thus, if an unauthorized request occurs, I will be throwing some for of Unauthorized exception which will ultimately result in a HTTP 403.
It all depends on what kind of authorization we are talking about and how much work is involved in making it happen.
Generally speaking it is not a good idea to mix your service layer with the web one so keep them separate.
Web Authorization is one thing - here you are saying I have a user, is he allowed to access this endpoint / page / whatever. If they are not, you save resources by not even calling the service layer at all and can reject the request in the web layer, very early in the process and very quickly without wasting resources.
Next, what is this service layer you are talking about? Is it the one that actually talks to the database or is it the one which does some data manipulation, once you have the data from the data layer?
My preference would be to have a separate datalayer and authorization layer. The authorization layer is the one that applies whatever rules you need and then returns the result of it so you know whether to even bother fetching any data or not. It is not linked to your web layer, it does not return HTTP codes and generally speaking has nothing to do with the UI side of things.
What this means is that if you have multiple clients, like for example a Web UI and a mobile UI then both will go through this layer so this level of work is not duplicated.
Even better you might want to build a proper API responsible for returning data then all you have to do is deal with this one thing from whatever client you have and you can handle your authorization there. If you make sure you can call your authorization layer separately from anything else and not depending on anything else then you can call it from multiple places and can test it easily and independently.
This is the kind of thing people think about before building anything, security is not the last thing you add in once everything else is done. You build your product with it in mind as it touches on a lot of things and it will influence your architecture.
A diagram of how everything fits together would help.
- What kind of architecture do you have?
- How involved is this authorization you are talking about?
- What kind of clients are we talking about?
There are lots and lots of questions to answer before this can work.
Just a couple more points from me, you made a couple of statements :
Service layer could get called by something other than a controller
(for example, services calling other services, which wouldn't be
concerned at that point with authorization).
Service authorization can be unit tested directly, rather than having
to rely on integration tests being written for each "layer" that goes
in front of the services.
if you are thinking about microservices, aka many little services which function independently then you most definitely need to concern yourself with security depending on how these things are accessed and communicate with each other.
that's why you write an authorization layer, everything goes through it and this is the one you test without caring about any UI kind of thing.