27

In the default AccountController created I see

    public AccountController()
        : this(Startup.UserManagerFactory(), Startup.OAuthOptions.AccessTokenFormat)
    {
    }

In Startup.Auth.cs I see

    UserManagerFactory = () => 
                new UserManager<IdentityUser>(new UserStore<IdentityUser>());   

Seems like the implementation of UserStore comes from Microsoft.AspNet.Identity.EntityFramework.

So, to customize the authentication do I have to implement my own version of UserStore like

 class MYSTUFFUserStore<IdentityUser> : UserStore<IdentityUser>
 {
 } 

and override the methods and then do this in Startup.Auth.cs

UserManagerFactory = () => 
               new UserManager<IdentityUser>(new MYSTUFFUserStore<IdentityUser>());   

I am looking for a correct way to customize the authentication.

sunil
  • 5,078
  • 6
  • 28
  • 33

1 Answers1

46

Assuming your table is called AppUser, convert your own AppUser domain object to IUser(using Microsoft.AspNet.Identity) like this

using Microsoft.AspNet.Identity;
public class AppUser : IUser
{
    //Existing database fields
    public long AppUserId { get; set; }
    public string AppUserName { get; set; }
    public string AppPassword { get; set; }

    public AppUser()
    {
        this.Id = Guid.NewGuid().ToString();  
    }

    [Ignore]
    public virtual string Id { get; set; }         
    [Ignore]
    public string UserName
    {
        get
        {
            return AppUserName;
        }
        set
        {
            AppUserName = value;
        }
    }
}

Implement the UserStore object like this

using Microsoft.AspNet.Identity;
public class UserStoreService 
         : IUserStore<AppUser>, IUserPasswordStore<AppUser>
{
    CompanyDbContext context = new CompanyDbContext();

    public Task CreateAsync(AppUser user)
    {            
        throw new NotImplementedException();
    }

    public Task DeleteAsync(AppUser user)
    {
        throw new NotImplementedException();
    }

    public Task<AppUser> FindByIdAsync(string userId)
    {
        throw new NotImplementedException();
    }

    public Task<AppUser> FindByNameAsync(string userName)
    {
        Task<AppUser> task = context.AppUsers.Where(
                              apu => apu.AppUserName == userName)
                              .FirstOrDefaultAsync();

        return task;
    }

    public Task UpdateAsync(AppUser user)
    {
        throw new NotImplementedException();
    }

    public void Dispose()
    {
        context.Dispose();
    }

    public Task<string> GetPasswordHashAsync(AppUser user)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }

        return Task.FromResult(user.AppPassword);
    }

    public Task<bool> HasPasswordAsync(AppUser user)
    {
        return Task.FromResult(user.AppPassword != null);
    }

    public Task SetPasswordHashAsync(AppUser user, string passwordHash)
    {
        throw new NotImplementedException();
    }

}

If you have your own custom password hashing you will also need to implement IPasswordHasher. Below is an example where there is no hashing of the password(Oh no!)

using Microsoft.AspNet.Identity;
public class MyPasswordHasher : IPasswordHasher
{
    public string HashPassword(string password)
    {
        return password;
    }

    public PasswordVerificationResult VerifyHashedPassword
                  (string hashedPassword, string providedPassword)
    {
        if (hashedPassword == HashPassword(providedPassword))
            return PasswordVerificationResult.Success;
        else
            return PasswordVerificationResult.Failed;
    }
}

In Startup.Auth.cs replace

UserManagerFactory = () => 
     new UserManager<IdentityUser>(new UserStore<IdentityUser>());

with

    UserManagerFactory = () => 
     new UserManager<AppUser>(new UserStoreService()) { PasswordHasher = new MyPasswordHasher() };

In ApplicationOAuthProvider.cs, replace IdentityUser with AppUser

In AccountController.cs, replace IdentityUser with AppUser and delete all the external authentication methods like GetManageInfo and RegisterExternal etc.

sunil
  • 5,078
  • 6
  • 28
  • 33
  • thanks for the answer, but how I can get `AppUser` from another controller for the current request? – Akram Berkawy Apr 10 '14 at 22:46
  • `User.Identity.Name` will give you the username in an authenticated request. – sunil Apr 11 '14 at 14:51
  • Sunil, can you explain why does custom class "AppUser" need to inplement built-in interface "IUser"? – Thomas.Benz Oct 14 '15 at 13:39
  • 1
    @Thomas.Benz here is a snippet from an article I found useful: "Microsoft.AspNet.Identity.UserManager where T: IUser This class allows to create, delete and find users as well as to create identities. An identity is created for a user and can be used to make authorization and authentication decisions." The idea here is to use some/most of the built-in features of ASP.NET Identity and then override/replace what you need with a custom implementation - at least that's what I have done here thanks to Sunil's answer – Trevor Jan 08 '17 at 21:20