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.