Proper Action Authorization and Validation

In this article I’ll describe a way to validate if only authorized users are allowed to perform operations.
Let’s consider a simple scenario where we have Contacts that are associated with Companies and the following rules apply:
– Rule 1: Users must always be associated with a single Company;
– Rule 2: Users can only change their own information, but cannot change the associated Company;
– Rule 3: Only users with Supervisor role can create Contacts and they must belong to Supervisor’s Company;
– Rule 4: Supervisors can edit all contacts belonging to their Company;
In each situation, user should receive an error message detailing the exact problem.
Both rules are both quite simple, but you could have much more complex scenarios.
I’m keeping it simple for the sake of explanation.
Keep in mind that, you should never bring do the frontend unnecessary logic or logic that the user can inspect, change and try to execute code in your application.
If we have those rules to implement it’s clear that we need logic to show the “Edit Contact” button accordingly, however not showing the buttons is not enough because the user can execute the action endpoints directly.
Rule 1: Users must always be associated with a single Company
This is quite simple as our Contact entity definition must set CompanyId as a mandatory field:

and by doing this, we can use built in form validation and write validation logic as we already saw in the previous article Validation is Golden Rule:
Please consider that all the following code will be defined in an Action named SaveContact in our Business Logic module:

Rule 2: Users can only change their own information, but cannot change the associated Company:

Rule 3: Only users with Supervisor role can create Contacts and they must belong to Supervisor’s Company:

Rule 4: Supervisors can edit all contacts belonging to their Company:

Did I miss something?
Actually I did. If you pay close attention, by implementing rules one-by-one we could potentially be missing edge cases. In this case we’re not validating that the user is a Supervisor or it’s the self user changing his information:

This is a fallback validation that must be executed after all others. Remember that we want to give the user the maximum information possible about the error and this last message only says that user is not allowed to perform the operation, and don’t say why.
We’ve being working bottom-up, beginning by defining our database entity and it’s constrains and validations in our core service module. Then we defined the authorization/validation logic in our Business Logic module and we still miss our Front End module.
In sake of simplicity I’ve bootstrapped Contact entity and in our Contacts list screen we have a button named “Add Contact”:
Contacts Page

We already know that only users with Supervisor role can see this button and by clicking it, user will be redirect to ContactDetail page with a ContactId = nullidentifier(), and this last one can lead to another problem that we’ll deal later.
Now in our Contacts page we define a new boolean variable named CanCreateContacts and we’ll set it on OnInitialize screen action:

and we assign the “Add Contact” button visibility condition to the CanCreateContacts local variable:

ContactDetail Page
Remember that i said that ContactDetail page is called submitting ContactId = nullidentifier() when we want to create a new contact. But we also know that this page can be used to create or edit contacts, so we cannot define that only Supervisors can access this page.
We need to be able to return an unauthorized error if any user but a Supervisor accesses this page submitting ContactId = nullidentifier().
To do so we will define a OnInitialize action in our ContactDetail page and use Javascript to validate if user has the Supervisor role:

Now, if any user without Supervisor role accesses the ContactDetail page without a ContactId, it will be redirected to Invalid Permissions page:

Not showing buttons it’s not enough!
Tiago