6

I'm learning about Clean and Vertical Slice Architecture for the first time and I'm having trouble understanding where Authorization and Authentication would fit in if we are using ASP.NET Core Identity. Also it feels as though, in some scenarios, separating the Identity User (with username, password, email etc), from any user related domain entity would be tricky.

For example, if we had a solution which used ASP.NET Core MVC, ASP.NET Core Identity, then an example project structure could be as follows:

Presentation/WebUI Project:

  • cshtml views / Razor pages would live here, along with controllers (if not using Razor pages).
  • The program/startup.cs for this project is where the extension methods from other layers could be called:
app.services.InjectInfrastructure(); // same for application

Infrastructure Project:

  • Implementations of application layer contracts.
  • Database contexts.
  • Perhaps implementations of repositories if you are using them.

Application Project:

  • Commands / queries (assuming using something like MassTransit.Mediator or MediatR).
  • Validators (for example with fluent validation).

Domain Project:

  • Domain entities (anaemic or with methods if following DDD).
  • Any aggregates, value objects etc (if using).

We could therefore have a scenario that has the following flow:

  1. Controller action invoked to get some data (representing the application layer query), which returns rendered html (cshtml view).
  2. Data is populated on this html page and a POST request (representing the application layer Command) is sent to a controller action.
  3. The command or query is sent using MediatR.
  4. Command handler runs (with data access such as dbcontext or repository), which validates, makes the appropriate changes to the data and returns a response.
  5. Response returned to the controller, which can then determine if the command/query has been successful
  6. Controller redirects to another action or populates ModelState errors.

Where I struggle to separate auth concerns is in a scenario where a user in the system has different roles and permissions depending on chocies they make on sign in.

For example, a education application where a teacher can select the school they are currently representing. In one school, they may have a certain role (for example head teacher) and in another they may have a role with lesser privellage.

In a scenario such as this, it seems like the Roles, Application Users (both identity concerns) are tightly coupled with the domain (which would house the different schools and roles that each school has).

My overarching question being, how would we implement this sort of Authentication/Authorization scenario using ASP.NET identity in a clean architecture fashion?

At the moment this scenario poses multiple problems:

  1. If we are to decouple Authentication / Authorization from the presentation layer, we cannot rely on the [Authorize(Role = "X")] [Authorize(Policy = "Y")] decorators on our controllers, as this logic should be delegated to infrastructure (to ensure if we wanted to swap presentation layer at any point, we do not need to rewrite authentication / authorization)
  2. The user in this scenario is tightly coupled to the domain logic, so I can only see it working if identity related entities and domain entities are squashed together in a single dbContext

Has anyone ever come across / implemented a system like this using clean architecture? Any insight anyone has on this would be great!

1
  • Could you please share the problem and complexity you are having with your application? Additionally are you following any sample or official document? Commented Apr 8, 2022 at 7:48

1 Answer 1

0

So you have authorization policies defined in domain layer?

You just need to wire up your "clean" authorization logic (policies) defined in domain to ASP.NET Core Authorization policies and continue using [Authorization] attribute.

authorizationOptions.AddPolicy(YourCleanPolicy.Name, policy =>
{
   policy.AddRequirements(new DomainPolicyRequirement(YourCleanPolicy.Name))
   //or alternatively
   policy.RequireAssertion(ctx => ...)
});

The RequirementHandler might go to the database and fetch additional user details if needed, but ideally you IPrincipal should contain all the claims needed. You can achieve this via IClaimsTransformation

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.