Best coding practices: where to put your authorization logic
Tags: security, webdev, cleancode
Published: 2024-04-03 11:02:11

Web application often have several layers which have different responsibilities. You'll almost always find the following three layers, but there could be more.
- Presentation layer: responsible for user interactions
- Business logic layer: handles functional requirements
- Infrastructure/persistence layer: responsible for handling data and databases

So where do we put our authorization logic?

Let's begin with the infrastructure/persistence layer. It is possible to add authorization here, because you could easily define the data a user should have access to. Still I don't think this is a good option for most applications for several reasons:
- Most of the times users should only have access to the data they need. So if a functional requirement changes you must add changes to a lot of code within the infrastructure/persistence layer.
- It is very easy to change something like adding a join in a SQL query and forgetting to change authorization accordingly.

What about the business logic layer? I think this is a better option, because in here you can define exactly what business actions a user should be allowed to have access to. Especially when a piece of logic should be executed from multiple applications. For example a desktop app and an Android app. But it has a big disadvantage:
- Some of your functions are called from several places. For example: function A calls B, C calls B and D calls A. So adding authorization could make things pretty hard and it's easy to forget to add it somewhere. 

How about the presentation layer? In my opinion this is the best place because of the following reasons:
- Many frameworks handle authorization here like [ASP.NET](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/simple?view=aspnetcore-8.0) , [FastApi](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/) and [Laravel](https://laravel.com/docs/11.x/authorization#authorizing-actions-via-gates).
- You can exactly see which data will be return by an endpoint or on a page so you can easily decide which users should have access.
- It's almost impossible to forget an authorization because you can easily browse your routes and see if every single on of them has authorization.
- Authorization is handled at the beginning of a request. No business logic (or very little) is executed yet so that lessens the chances of introducing a [business logic vulnerability](https://owasp.org/www-community/vulnerabilities/Business_logic_vulnerability).

It would look something like this:

```csharp
[Authorize]//makes sure a user is logged in
[Route("api/[controller]")]
[ApiController]
public class ValuesController(MyAuthorizationValidator myAuthorizationValidator) : ControllerBase
{
	private readonly MyAuthorizationValidator validator = myAuthorizationValidator;

	// GET: api/values
	[HttpGet]
	public ActionResult<IEnumerable<string>> Get()
	{
		//other authorization rules which could not be handled by [Authorize]
		if (!validator.IsAllowedAccordingToMyCustomAuthRule(HttpContext.User))
		{
			return Unauthorized();
		}
		//Run business logic
		return new string[] { "value1", "value2" };
	}
}
```


Do you prefer to put authorization logic in a different place? Or do you have other reasons to put it in the presentation layer? Please let me know in the comments!