httpflow icon indicating copy to clipboard operation
httpflow copied to clipboard

Multi user project.

Open mah0o7co opened this issue 2 years ago • 5 comments

Hi where should i put my identity codes that doesn't ruin architecture?

mah0o7co avatar Nov 22 '21 08:11 mah0o7co

Hi where should i put my identity codes that doesn't ruin architecture?

Still haven't found a concrete solution for that too. These architecture frameworks are interesting but the authors don't understand that real world examples should be provided so that they can be useful for people. Using Identity and isolating it 100% from domain is a headache. Try to check this and this.

HybridSolutions avatar Dec 17 '21 01:12 HybridSolutions

This is a solution template. If you want to see a reference application that uses similar, check out the eShopOnWeb app here:

https://github.com/dotnet-architecture/eShopOnWeb

Read the associated (free) eBook, too: https://ardalis.com/architecture-ebook/

ardalis avatar Dec 17 '21 02:12 ardalis

That said, it is my intention to extend this template to support identity, since it's a very common requirement. The biggest thing that's prevented me from doing so is that typically there's a big difference between how you should do auth for a forms/views based app (MVC or Razor Pages) and an API app (Controllers or Web API endpoints or Minimal APIs). The former typically uses cookies; the latter tokens. Combining both in one app can be troublesome, so typically I try to split the APIs from the Pages.

ardalis avatar Dec 17 '21 02:12 ardalis

This is a solution template. If you want to see a reference application that uses similar, check out the eShopOnWeb app here:

https://github.com/dotnet-architecture/eShopOnWeb

Read the associated (free) eBook, too: https://ardalis.com/architecture-ebook/

I know that application but the integration with Identity does not address real world usages. This architecture needs a full example of how to use Identity fully isolated and working with a custom User entity in Domain layer. Real world web applications that require Authorization and Authentication must store additional user information and also manage that information, manage users and their roles. Extending the Identity tables directly would solve the problem, but I believe that custom user properties should not be tight to .Net Identity and it's tables ensuring future scalability.

This article follows this principle but is not following Clean Architecture so a couple of things might not work.

Jason Taylor's approach seems one of the few that tries to tackle this Identity isolation problem with success, but unfortunately, it does not address custom user information so isolating Identity was an "easy task" despite the fact that using this code

using CleanArchitecture.Application.Common.Models;

namespace CleanArchitecture.Application.Common.Interfaces;

public interface IIdentityService
{
    Task<string> GetUserNameAsync(string userId);

    Task<bool> IsInRoleAsync(string userId, string role);

    Task<bool> AuthorizeAsync(string userId, string policyName);

    Task<(Result Result, string UserId)> CreateUserAsync(string userName, string password);

    Task<Result> DeleteUserAsync(string userId);
}

brings some issues that I'm not sure it's the right way to go since it cuts out Identity features. I'm talking about that CreateUserAsync that had to use a crippled custom made Result object instead of using the original IdentityResult object that offers an IEnumerable<T> of IdentityError. Since you can't have Identity in Application layer, we have an issue here. Dead end? 😕 Also, that same method can't receive a ApplicationUser parameter and I'm not sure if that solution is the right way to go. With a custom user, it could be necessary to have a method with many parameters:

Task<(Result Result, string UserId)> CreateUserAsync(string userName, string password, string jobPosition, DateTime birthday, string photo, ...);

In this case, my alternative is to make this method receive a UserProfile object:

Task<(Result result, string UserId)> CreateUserAsync(UserProfile userProfile);

In the implementation I then have:

public async Task<(Result result, string userId)> CreateUserAsync(UserProfile userProfile)
        {
            var user = new ApplicationUser
            {
                UserName = userProfile.Username,
                Email = userProfile.Email,
                UserProfile = userProfile,
            };

            var result = await _userManager.CreateAsync(user, userProfile.Password);

UserProfile is inserted automatically by EF into the UserProfile table in database. Is this the right path? Should I override UserStore instead, and keep using the original CreateUserAsync method from UserManager and ditch this IdentityUserService approach?

Jason's approach does not address the best way to work with Identity and a custom User class (UserProfile) and how and where should we make sure syncing works when creating and deleting a Identity user + it's user profile dependency.

@ardalis please take no offense on my quest. I love the architecture, your valuable contribution to the community 🙌 but I just wish someone would take this matter by the horns 😃 since Microsoft won't likely do it.

I already have a working solution with User Profile (UsersProfile table) linked to Identity Users (AspNetUsers) but I'm stuck in some required decisions on how to proceed. If I send you the project, do you think you could take a look and maybe help me solve this? You are far more experienced than me on these kind of architecture issues.

Once again, sorry for taking it out on you but this is driving me crazy. I really would love to have Clean Architecture implemented the right way.

Thank you!

HybridSolutions avatar Dec 17 '21 12:12 HybridSolutions

With regards to tokens vs cookies, maybe consider the BFF pattern? It is well described here: https://auth0.com/blog/backend-for-frontend-pattern-with-auth0-and-dotnet/

Tokens by themselves push developers to look to store them insecurely, and they'll mostly end up storing them in local storage, if it's a SPA or mobile application consuming the APIs. HttpOnly secure cookies are the only place that kind of information should be stored, and ideally the token is stored in a token cache, and the cookie stores a reference to it (See https://youtu.be/YXdJ2HLAOdE?t=2771)

I think realistically, you really have two types of web host, one pure API and the other the UI. In terms of clean architecture and separation of concerns, one would probably want to design and deploy them separately anyway.

rebeccapowell avatar Oct 12 '22 20:10 rebeccapowell