Custom ASP.NET Identity 2.0 UserStore - Is implementing all interfaces required? Custom ASP.NET Identity 2.0 UserStore - Is implementing all interfaces required? asp.net asp.net

Custom ASP.NET Identity 2.0 UserStore - Is implementing all interfaces required?


Actually the IUserTwoFactorStore interface is really simple, so far my implementation (I don't use two factor auth either) is this:

 .... public Task<bool> GetTwoFactorEnabledAsync(User user) {     return Task.FromResult(false); } public Task SetTwoFactorEnabledAsync(User user, bool enabled) {     throw new NotImplementedException(); }

It works, although I just did it couple minutes ago and didn't test whole app thoroughly.


I had the same problem. For the moment, as the SignInManager.SignInOrTwoFactor method blindly checks for the GetTwoFactorAuthentication it throws an exception when the UserStore doesn't implement the IUserTwoFactorStore.

I believe Microsoft intended that the SignInManager PasswordSignInAsync method must be overriden in a custom SignInManager class. Unfortunately I couldn't find any documentation or samples pointing so.

Here is the SignInManager wrapper class I implemented to solve this issue:

public class EnhancedSignInManager<TUser, TKey> : SignInManager<TUser, TKey>    where TUser : class, IUser<TKey>    where TKey : IEquatable<TKey>{    public EnhancedSignInManager(        UserManager<TUser, TKey> userManager,         IAuthenticationManager authenticationManager)        : base(userManager, authenticationManager)    {    }    public override async Task SignInAsync(        TUser user,         bool isPersistent,         bool rememberBrowser)    {        var userIdentity = await CreateUserIdentityAsync(user).WithCurrentCulture();        // Clear any partial cookies from external or two factor partial sign ins        AuthenticationManager.SignOut(            DefaultAuthenticationTypes.ExternalCookie,             DefaultAuthenticationTypes.TwoFactorCookie);        if (rememberBrowser)        {            var rememberBrowserIdentity = AuthenticationManager                .CreateTwoFactorRememberBrowserIdentity(ConvertIdToString(user.Id));            AuthenticationManager.SignIn(                new AuthenticationProperties { IsPersistent = isPersistent },                 userIdentity,                 rememberBrowserIdentity);        }        else        {            AuthenticationManager.SignIn(                new AuthenticationProperties { IsPersistent = isPersistent },                 userIdentity);        }    }    private async Task<SignInStatus> SignInOrTwoFactor(TUser user, bool isPersistent)    {        var id = Convert.ToString(user.Id);        if (UserManager.SupportsUserTwoFactor             && await UserManager.GetTwoFactorEnabledAsync(user.Id)                                .WithCurrentCulture()            && (await UserManager.GetValidTwoFactorProvidersAsync(user.Id)                                 .WithCurrentCulture()).Count > 0                && !await AuthenticationManager.TwoFactorBrowserRememberedAsync(id)                                               .WithCurrentCulture())        {            var identity = new ClaimsIdentity(                DefaultAuthenticationTypes.TwoFactorCookie);            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));            AuthenticationManager.SignIn(identity);            return SignInStatus.RequiresVerification;        }        await SignInAsync(user, isPersistent, false).WithCurrentCulture();        return SignInStatus.Success;    }    public override async Task<SignInStatus> PasswordSignInAsync(        string userName,         string password,         bool isPersistent,         bool shouldLockout)    {        if (UserManager == null)        {            return SignInStatus.Failure;        }        var user = await UserManager.FindByNameAsync(userName).WithCurrentCulture();        if (user == null)        {            return SignInStatus.Failure;        }        if (UserManager.SupportsUserLockout             && await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())        {            return SignInStatus.LockedOut;        }        if (UserManager.SupportsUserPassword             && await UserManager.CheckPasswordAsync(user, password)                                .WithCurrentCulture())        {            return await SignInOrTwoFactor(user, isPersistent).WithCurrentCulture();        }        if (shouldLockout && UserManager.SupportsUserLockout)        {            // If lockout is requested, increment access failed count            // which might lock out the user            await UserManager.AccessFailedAsync(user.Id).WithCurrentCulture();            if (await UserManager.IsLockedOutAsync(user.Id).WithCurrentCulture())            {                return SignInStatus.LockedOut;            }        }        return SignInStatus.Failure;    }}

I hope it helps. Cheers


I had the same problem and I don't want to implement the IUserTwoFactorStore<TUser, TKey> just to say that I don't implement it. But I also don't want to go back and muck around if I end up wanting to implement it (which I anticipate I will). So what I consider a future proof (and reusable) solution would be: (inspired by @gjsduarte's answer)

public class SafeUserManager<TUser, TKey> : UserManager<TUser, TKey>{    public override Task<bool> GetTwoFactorEnabledAsync(TKey userId)    {        return Store is IUserTwoFactorStore<TUser, TKey>            ? base.GetTwoFactorEnabledAsync(userId)            : Task.FromResult(false);    }}

It would probably be a good idea to do the same for the other Get[feature]EnabledAsync(TKey userId) methods.