MVC 5 IoC and Authentication

10,498

Solution 1

I ended up deciding to implement the IUserStore, IUserStore, IUserPasswordStore, IUserLoginStore, to be able to move the UserRepository down into it's rightful place, the DataAccess Layer. But still get the Security Benifits of the Owin and new Identity Framework.

It's quite easy to implement, and doesn't take much to abstract it. Here is a taste of the UserStoreWrapper

namespace qubis.booking.WebApp.App_Code.Identity
{
    public class UserServiceWrapper : IUserStore<ApplicationUserWrapper>, 
                                      IUserPasswordStore<ApplicationUserWrapper>, 
                                      IUserLoginStore<ApplicationUserWrapper>
    {
        public IUserRepository UserRepos { get; set; } // My own Interface.
        public UserServiceWrapper(IUserRepository userRepo)
        {
            UserRepos = userRepo;
        }


        public async Task CreateAsync(ApplicationUserWrapper user)
        {
            UserRepos.Insert(user.RealUser);
        }

        public async Task<ApplicationUserWrapper> FindByIdAsync(string userId)
        {
            var appUser = UserRepos.FindByUserName(userId);
            ApplicationUserWrapper wrappedUser;
            if (appUser != null)
            {
                wrappedUser = new ApplicationUserWrapper(appUser);
            }
            else
                wrappedUser = null;
            return wrappedUser;
        }

In the Account controller I Simply just ask for it to be injected:

public AccountController(UserManager<ApplicationUserWrapper> userManager)
{
    UserManager = userManager;{ AllowOnlyAlphanumericUserNames = false };
}

And as I am using Ninject I just set it upin the kernel like so:

// <summary>
// Load your modules or register your services here!
// </summary>
// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IUserStore<ApplicationUserWrapper>>().To<UserServiceWrapper>();
    kernel.Bind<UserManager<ApplicationUserWrapper>>().ToSelf();
}

To see the Identity frameworks structure, please see this article. http://www.asp.net/identity/overview/extensibility/implementing-a-custom-mysql-aspnet-identity-storage-provider

Solution 2

Since this is .NET, the standard approach to security is to authenticate at the application boundary, and convert the authentication information into an IPrincipal. MVC supports this out of the box.

If you need other information gained during authentication, you can gather that at in the Composition Root and use it to compose your services.

As an example, imagine that you need the authenticated user's email address in a lower layer. Any class that requires the user's email address can simply request it as a Concrete Dependency:

public class EmailThingy
{
    private readonly string userEmail;

    public EmailThingy(string userEmail)
    {
        if (userEmail == null)
            throw new ArgumentNullException("userEmail");

        this.userEmail = userEmail;
    }

    // other members go here...
}

In ASP.NET MVC, the Composition Root is IControllerFactory. IIRC, you can pull the authentication data from within the CreateController method and use it to compose your object graph.

These days, I use IPrincipal in the same way: I inject it as a dependency, instead of relying on the Thread.CurrentPrincipal Ambient Context, because it's easier to unit test when everything is consistently injected via Constructor Injection.

Solution 3

You might be interested to get a look at Thinktecture.IdentityServer.v2 https://github.com/thinktecture/Thinktecture.IdentityServer.v2. Many of your concerns are already implemented and encapsulated. If you don't find what you need you'll have to think about how to abstract all these concerns and implement it on your own.

Share:
10,498
André Snede
Author by

André Snede

Senior Software Consultant, specializing in cloud architecture and C# software development. See my developer blog at https://snede.net. Hire me at https://qubis.dk.

Updated on June 07, 2022

Comments

  • André Snede
    André Snede almost 2 years

    I am just about to start on a project, where I will be using MVC5. But as I want to use IoC and later reuse my user tables, and add custom stuff to it, I am finding it very hard to see how I can use the new Identity framework that came with MVC5.

    I am more and more looking towards basic forms auth. What are your solutions?

    My needs:

    • User repository/service must be injected
    • User repository must reside in the DAL
    • User repository must be able to support other technologies than EF
    • Authentication with OpenID and OAuth must be somewhat easy to implement
    • MUST BE SECURE
    • Should be reusable in other projects, eg. WPF

    I have been looking for a long time for an answer, but everything I see is hardcoded in the controller.

    How are you solving this? Are you writing most from scratch, or can you bind into something that will scale to other .NET platforms as WCF and WPF?

    The below code is taken directly from the AccountController in the default ASP.NET MVC 5 Template. The first thing it does is a Bastard Injection.

    [Authorize]
    public class AccountController : Controller
    {
        public AccountController()
            : this(
                new UserManager<ApplicationUser>(
                    new UserStore<ApplicationUser>(
                        new ApplicationDbContext())))
        {
        }
    
        public AccountController(UserManager<ApplicationUser> userManager)
        {
            UserManager = userManager;
        }
    }
    

    The accepted answer will go to the person, that shows me what they have done, that incorporates the above requirements

  • André Snede
    André Snede over 10 years
    Thanks for your answer, but I'm not looking for another dependency. I would like to get as much out of what ASP.NET offers me. Meanwhile being able to bend it to my will.
  • Tomasz Jaskuλa
    Tomasz Jaskuλa over 10 years
    I think it might be worthwhile to look how they dealt with it and if you want you can pick some ideas for your own work.
  • André Snede
    André Snede over 10 years
    I see your point, and I would probably learn alot from diving into their code. But I would rather have a book or other resource, that explains the concepts, best practices, and the do's, why's and don'ts.
  • André Snede
    André Snede over 10 years
    Thanks Mark. That answered alot of my questions. But can you elaborate on how I best implement a Authentication system, that can scale out. As I feel that the new Identity framework and UserManager in ASP.NET MVC 5 is heavily dependent on EntityFramework. The whole user data and authentication logic (matching passwords, etc.) is something I would like to have abstracted down to the domain layer and the DAL. So I can reuse it in a WCF or WPF application later.
  • Mark Seemann
    Mark Seemann over 10 years
    What do you mean by Authentication system? Username/password? OAuth? OpenId? Facebook authentication? Two-factor? Claims-based?
  • André Snede
    André Snede over 10 years
    I want to be able to implement my own User Service, where I am in charge of checking the passwords and everything, but where I can keep as much functionality from the MVC goodiebag as possible. In short I want to be able to inject, the authentication, instead of using the default setup from the MVC template. And I'm wondering how people are doing this. I want to be able to use the same authentication mechanics in other projects as well, and even change it, if I see fit in the future.
  • André Snede
    André Snede over 10 years
    Basicly I want to be able to inject some authentication system, that I can use elsewhere as well, in a NON ASP.NET project.
  • Mark Seemann
    Mark Seemann over 10 years
    At Grean, we just use Auth0. Authentication is a really complex domain, and better left for domain experts. In Grean, two out or four partners are former authentication and authorization experts, and even we don't want to have to deal with all that complexity.
  • André Snede
    André Snede over 10 years
    I could see my self use Windows Authentication in some scenarios, and others a custom one. I can't really see that an external Auth Library would give me much benefit. I feel locked down, more than freed.
  • Robert
    Robert over 10 years
    What is "ApplicationUserWrapper"? Would you be able to share the complete implementation? Like for instance what does your AccountController look like after the fact? Are there any other gotchas that you ran into after implementing this approach?
  • janhartmann
    janhartmann almost 10 years
    @André Snede Hansen: Is it possible you can share some more code? I am in the need of this separation as well!
  • André Snede
    André Snede almost 10 years
    @meep Might be able to later.
  • janhartmann
    janhartmann almost 10 years
    @André Snede Hansen: Many thanks! (Danish: Tak, du kan evt. fange mig på [email protected], hvis det er ;-))
  • M Kenyon II
    M Kenyon II almost 9 years
    I'm curious to see what UserRepos looks like. Since I have a custom database with user info, that's where I'm getting stuck.
  • André Snede
    André Snede almost 9 years
    @MKenyonII I will look into sharing an example with you.