André Snede
André Snede

Reputation: 10045

MVC 5 IoC and Authentication

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:

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

Upvotes: 16

Views: 10669

Answers (4)

fernet
fernet

Reputation: 103

If all you need is to inject custom UserStore implementation this article may help you

Basically you need to inject this (depends if you want to use roles, claims etc..):

  1. Write a User class that implements the IUser interface

    public class IdentityUser : IUser {
        public IdentityUser(){...}
        public IdentityUser(string userName) (){...}
        public string Id { get; set; }
        public string UserName { get; set; }
        public string PasswordHash { get; set; }
        public string SecurityStamp { get; set; }  
    }
    
  2. Write a User store class that implements the IUserStore, IUserClaimStore, IUserLoginStore, IUserRoleStore and IUserPasswordStore

    public class UserStore : IUserStore<IdentityUser>,
        IUserClaimStore<IdentityUser>,
        IUserLoginStore<IdentityUser>,
        IUserRoleStore<IdentityUser>,
        IUserPasswordStore<IdentityUser> {
    
        public UserStore(){...}
        public Task CreateAsync(IdentityUser user){...}
        public Task<IdentityUser> FindByIdAsync(string userId){...}   
        .. .
    }
    

Upvotes: 0

Andr&#233; Snede
Andr&#233; Snede

Reputation: 10045

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

Upvotes: 3

Mark Seemann
Mark Seemann

Reputation: 233170

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.

Upvotes: 8

Tomasz Jaskuλa
Tomasz Jaskuλa

Reputation: 16013

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.

Upvotes: 3

Related Questions