// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; using Xunit; namespace Microsoft.AspNetCore.Identity.Test { public class UserManagerTest { [Fact] public void EnsureDefaultServicesDefaultsWithStoreWorks() { var services = new ServiceCollection() .AddTransient, NoopUserStore>(); services.AddIdentity(); services.AddSingleton(); services.AddLogging(); var manager = services.BuildServiceProvider().GetRequiredService>(); Assert.NotNull(manager.PasswordHasher); Assert.NotNull(manager.Store); Assert.NotNull(manager.Options); } [Fact] public void AddUserManagerWithCustomManagerReturnsSameInstance() { var services = new ServiceCollection() .AddTransient, NoopUserStore>() .AddSingleton(); services.AddLogging(); services.AddIdentity() .AddUserManager() .AddRoleManager(); var provider = services.BuildServiceProvider(); Assert.Same(provider.GetRequiredService>(), provider.GetRequiredService()); Assert.Same(provider.GetRequiredService>(), provider.GetRequiredService()); } public class CustomUserManager : UserManager { public CustomUserManager() : base(new Mock>().Object, null, null, null, null, null, null, null, null) { } } public class CustomRoleManager : RoleManager { public CustomRoleManager() : base(new Mock>().Object, null, null, null, null, null) { } } [Fact] public async Task CreateCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; store.Setup(s => s.CreateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.UserName)).Verifiable(); store.Setup(s => s.SetNormalizedUserNameAsync(user, user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.CreateAsync(user); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task CreateCallsUpdateEmailStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo", Email = "Foo@foo.com" }; store.Setup(s => s.CreateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.UserName)).Verifiable(); store.Setup(s => s.GetEmailAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.Email)).Verifiable(); store.Setup(s => s.SetNormalizedEmailAsync(user, user.Email.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); store.Setup(s => s.SetNormalizedUserNameAsync(user, user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.CreateAsync(user); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task DeleteCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; store.Setup(s => s.DeleteAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.DeleteAsync(user); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task UpdateCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.UserName)).Verifiable(); store.Setup(s => s.SetNormalizedUserNameAsync(user, user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.UpdateAsync(user); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task UpdateWillUpdateNormalizedEmail() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo", Email = "email" }; store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.UserName)).Verifiable(); store.Setup(s => s.GetEmailAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.Email)).Verifiable(); store.Setup(s => s.SetNormalizedUserNameAsync(user, user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); store.Setup(s => s.SetNormalizedEmailAsync(user, user.Email.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.UpdateAsync(user); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task SetUserNameCallsStore() { // Setup var store = new Mock>(); var user = new TestUser(); store.Setup(s => s.SetUserNameAsync(user, "foo", CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult("foo")).Verifiable(); store.Setup(s => s.SetNormalizedUserNameAsync(user, "FOO", CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.SetUserNameAsync(user, "foo"); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task FindByIdCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; store.Setup(s => s.FindByIdAsync(user.Id, CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.FindByIdAsync(user.Id); // Assert Assert.Equal(user, result); store.VerifyAll(); } [Fact] public async Task FindByNameCallsStoreWithNormalizedName() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; store.Setup(s => s.FindByNameAsync(user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.FindByNameAsync(user.UserName); // Assert Assert.Equal(user, result); store.VerifyAll(); } [Fact] public async Task CanFindByNameCallsStoreWithoutNormalizedName() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; store.Setup(s => s.FindByNameAsync(user.UserName, CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); userManager.KeyNormalizer = null; // Act var result = await userManager.FindByNameAsync(user.UserName); // Assert Assert.Equal(user, result); store.VerifyAll(); } [Fact] public async Task FindByEmailCallsStoreWithNormalizedEmail() { // Setup var store = new Mock>(); var user = new TestUser { Email = "Foo" }; store.Setup(s => s.FindByEmailAsync(user.Email.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.FindByEmailAsync(user.Email); // Assert Assert.Equal(user, result); store.VerifyAll(); } [Fact] public async Task CanFindByEmailCallsStoreWithoutNormalizedEmail() { // Setup var store = new Mock>(); var user = new TestUser { Email = "Foo" }; store.Setup(s => s.FindByEmailAsync(user.Email, CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); userManager.KeyNormalizer = null; // Act var result = await userManager.FindByEmailAsync(user.Email); // Assert Assert.Equal(user, result); store.VerifyAll(); } [Fact] public async Task AddToRolesCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var roles = new string[] { "A", "B", "C", "C" }; store.Setup(s => s.AddToRoleAsync(user, "A", CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.AddToRoleAsync(user, "B", CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.AddToRoleAsync(user, "C", CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "A", CancellationToken.None)) .Returns(Task.FromResult(false)) .Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "B", CancellationToken.None)) .Returns(Task.FromResult(false)) .Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "C", CancellationToken.None)) .Returns(Task.FromResult(false)) .Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.AddToRolesAsync(user, roles); // Assert Assert.True(result.Succeeded); store.VerifyAll(); store.Verify(s => s.AddToRoleAsync(user, "C", CancellationToken.None), Times.Once()); } [Fact] public async Task AddToRolesFailsIfUserInRole() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var roles = new[] { "A", "B", "C" }; store.Setup(s => s.AddToRoleAsync(user, "A", CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "B", CancellationToken.None)) .Returns(Task.FromResult(true)) .Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.AddToRolesAsync(user, roles); // Assert IdentityResultAssert.IsFailure(result, new IdentityErrorDescriber().UserAlreadyInRole("B")); store.VerifyAll(); } [Fact] public async Task RemoveFromRolesCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var roles = new[] { "A", "B", "C" }; store.Setup(s => s.RemoveFromRoleAsync(user, "A", CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.RemoveFromRoleAsync(user, "B", CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.RemoveFromRoleAsync(user, "C", CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "A", CancellationToken.None)) .Returns(Task.FromResult(true)) .Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "B", CancellationToken.None)) .Returns(Task.FromResult(true)) .Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "C", CancellationToken.None)) .Returns(Task.FromResult(true)) .Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.RemoveFromRolesAsync(user, roles); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task RemoveFromRolesFailsIfNotInRole() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var roles = new string[] { "A", "B", "C" }; store.Setup(s => s.RemoveFromRoleAsync(user, "A", CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "A", CancellationToken.None)) .Returns(Task.FromResult(true)) .Verifiable(); store.Setup(s => s.IsInRoleAsync(user, "B", CancellationToken.None)) .Returns(Task.FromResult(false)) .Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.RemoveFromRolesAsync(user, roles); // Assert IdentityResultAssert.IsFailure(result, new IdentityErrorDescriber().UserNotInRole("B")); store.VerifyAll(); } [Fact] public async Task AddClaimsCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var claims = new Claim[] { new Claim("1", "1"), new Claim("2", "2"), new Claim("3", "3") }; store.Setup(s => s.AddClaimsAsync(user, claims, CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.AddClaimsAsync(user, claims); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task AddClaimCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var claim = new Claim("1", "1"); store.Setup(s => s.AddClaimsAsync(user, It.IsAny>(), CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.AddClaimAsync(user, claim); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task UpdateClaimCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var claim = new Claim("1", "1"); var newClaim = new Claim("1", "2"); store.Setup(s => s.ReplaceClaimAsync(user, It.IsAny(), It.IsAny(), CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.ReplaceClaimAsync(user, claim, newClaim); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task CheckPasswordWillRehashPasswordWhenNeeded() { // Setup var store = new Mock>(); var hasher = new Mock>(); var user = new TestUser { UserName = "Foo" }; var pwd = "password"; var hashed = "hashed"; var rehashed = "rehashed"; store.Setup(s => s.GetPasswordHashAsync(user, CancellationToken.None)) .ReturnsAsync(hashed) .Verifiable(); store.Setup(s => s.SetPasswordHashAsync(user, It.IsAny(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); store.Setup(x => x.UpdateAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(IdentityResult.Success)); hasher.Setup(s => s.VerifyHashedPassword(user, hashed, pwd)).Returns(PasswordVerificationResult.SuccessRehashNeeded).Verifiable(); hasher.Setup(s => s.HashPassword(user, pwd)).Returns(rehashed).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); userManager.PasswordHasher = hasher.Object; // Act var result = await userManager.CheckPasswordAsync(user, pwd); // Assert Assert.True(result); store.VerifyAll(); hasher.VerifyAll(); } [Fact] public async Task CreateFailsWithNullSecurityStamp() { // Setup var store = new Mock>(); var manager = MockHelpers.TestUserManager(store.Object); var user = new TestUser { UserName = "nulldude" }; store.Setup(s => s.GetSecurityStampAsync(user, It.IsAny())).ReturnsAsync(default(string)).Verifiable(); // Act // Assert var ex = await Assert.ThrowsAsync(() => manager.CreateAsync(user)); Assert.Contains(Resources.NullSecurityStamp, ex.Message); store.VerifyAll(); } [Fact] public async Task UpdateFailsWithNullSecurityStamp() { // Setup var store = new Mock>(); var manager = MockHelpers.TestUserManager(store.Object); var user = new TestUser { UserName = "nulldude" }; store.Setup(s => s.GetSecurityStampAsync(user, It.IsAny())).ReturnsAsync(default(string)).Verifiable(); // Act // Assert var ex = await Assert.ThrowsAsync(() => manager.UpdateAsync(user)); Assert.Contains(Resources.NullSecurityStamp, ex.Message); store.VerifyAll(); } [Fact] public async Task RemoveClaimsCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var claims = new Claim[] { new Claim("1", "1"), new Claim("2", "2"), new Claim("3", "3") }; store.Setup(s => s.RemoveClaimsAsync(user, claims, CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.RemoveClaimsAsync(user, claims); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task RemoveClaimCallsStore() { // Setup var store = new Mock>(); var user = new TestUser { UserName = "Foo" }; var claim = new Claim("1", "1"); store.Setup(s => s.RemoveClaimsAsync(user, It.IsAny>(), CancellationToken.None)) .Returns(Task.FromResult(0)) .Verifiable(); store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); // Act var result = await userManager.RemoveClaimAsync(user, claim); // Assert Assert.True(result.Succeeded); store.VerifyAll(); } [Fact] public async Task CheckPasswordWithNullUserReturnsFalse() { var manager = MockHelpers.TestUserManager(new EmptyStore()); Assert.False(await manager.CheckPasswordAsync(null, "whatevs")); } [Fact] public void UsersQueryableFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsQueryableUsers); Assert.Throws(() => manager.Users.Count()); } [Fact] public async Task UsersEmailMethodsFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserEmail); await Assert.ThrowsAsync(() => manager.FindByEmailAsync(null)); await Assert.ThrowsAsync(() => manager.SetEmailAsync(null, null)); await Assert.ThrowsAsync(() => manager.GetEmailAsync(null)); await Assert.ThrowsAsync(() => manager.IsEmailConfirmedAsync(null)); await Assert.ThrowsAsync(() => manager.ConfirmEmailAsync(null, null)); } [Fact] public async Task UsersPhoneNumberMethodsFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserPhoneNumber); await Assert.ThrowsAsync(async () => await manager.SetPhoneNumberAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.SetPhoneNumberAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.GetPhoneNumberAsync(null)); } [Fact] public async Task TokenMethodsThrowWithNoTokenProvider() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); var user = new TestUser(); await Assert.ThrowsAsync( async () => await manager.GenerateUserTokenAsync(user, "bogus", null)); await Assert.ThrowsAsync( async () => await manager.VerifyUserTokenAsync(user, "bogus", null, null)); } [Fact] public async Task PasswordMethodsFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserPassword); await Assert.ThrowsAsync(() => manager.CreateAsync(null, null)); await Assert.ThrowsAsync(() => manager.ChangePasswordAsync(null, null, null)); await Assert.ThrowsAsync(() => manager.AddPasswordAsync(null, null)); await Assert.ThrowsAsync(() => manager.RemovePasswordAsync(null)); await Assert.ThrowsAsync(() => manager.CheckPasswordAsync(null, null)); await Assert.ThrowsAsync(() => manager.HasPasswordAsync(null)); } [Fact] public async Task SecurityStampMethodsFailWhenStoreNotImplemented() { var store = new Mock>(); store.Setup(x => x.GetUserIdAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(Guid.NewGuid().ToString())); var manager = MockHelpers.TestUserManager(store.Object); Assert.False(manager.SupportsUserSecurityStamp); await Assert.ThrowsAsync(() => manager.UpdateSecurityStampAsync(null)); await Assert.ThrowsAsync(() => manager.GetSecurityStampAsync(null)); await Assert.ThrowsAsync( () => manager.VerifyChangePhoneNumberTokenAsync(null, "1", "111-111-1111")); await Assert.ThrowsAsync( () => manager.GenerateChangePhoneNumberTokenAsync(null, "111-111-1111")); } [Fact] public async Task LoginMethodsFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserLogin); await Assert.ThrowsAsync(async () => await manager.AddLoginAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.RemoveLoginAsync(null, null, null)); await Assert.ThrowsAsync(async () => await manager.GetLoginsAsync(null)); await Assert.ThrowsAsync(async () => await manager.FindByLoginAsync(null, null)); } [Fact] public async Task ClaimMethodsFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserClaim); await Assert.ThrowsAsync(async () => await manager.AddClaimAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.ReplaceClaimAsync(null, null, null)); await Assert.ThrowsAsync(async () => await manager.RemoveClaimAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.GetClaimsAsync(null)); } private class ATokenProvider : IUserTwoFactorTokenProvider { public Task CanGenerateTwoFactorTokenAsync(UserManager manager, TestUser user) { throw new NotImplementedException(); } public Task GenerateAsync(string purpose, UserManager manager, TestUser user) { throw new NotImplementedException(); } public Task ValidateAsync(string purpose, string token, UserManager manager, TestUser user) { throw new NotImplementedException(); } } [Fact] public void UserManagerWillUseTokenProviderInstance() { var services = new ServiceCollection(); var provider = new ATokenProvider(); services.AddLogging() .AddIdentity(o => o.Tokens.ProviderMap.Add("A", new TokenProviderDescriptor(typeof(ATokenProvider)) { ProviderInstance = provider })).AddUserStore(); var manager = services.BuildServiceProvider().GetService>(); Assert.ThrowsAsync(() => manager.GenerateUserTokenAsync(new TestUser(), "A", "purpose")); } [Fact] public void TOTPTest() { //var verify = new TotpAuthenticatorVerification(); //var secret = "abcdefghij"; //var secret = Base32.FromBase32(authKey); // Assert.Equal(bytes, secret); //var code = verify.VerifyCode(secret, -1); //Assert.Equal(code, 287004); //var bytes = new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)'!', (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF }; //var base32 = Base32.ToBase32(bytes); // var code = Rfc6238AuthenticationService.GenerateCode(bytes); // Assert.Equal(Rfc6238AuthenticationService.GenerateCode(bytes), Rfc6238AuthenticationService.CalculateOneTimePassword(new HMACSHA1(bytes))); //Assert.True(Rfc6238AuthenticationService.ValidateCode(bytes, code)); } public static byte[] ToBytes(string input) { if (string.IsNullOrEmpty(input)) { throw new ArgumentNullException("input"); } input = input.TrimEnd('='); //remove padding characters int byteCount = input.Length * 5 / 8; //this must be TRUNCATED byte[] returnArray = new byte[byteCount]; byte curByte = 0, bitsRemaining = 8; int mask = 0, arrayIndex = 0; foreach (char c in input) { int cValue = CharToValue(c); if (bitsRemaining > 5) { mask = cValue << (bitsRemaining - 5); curByte = (byte)(curByte | mask); bitsRemaining -= 5; } else { mask = cValue >> (5 - bitsRemaining); curByte = (byte)(curByte | mask); returnArray[arrayIndex++] = curByte; curByte = (byte)(cValue << (3 + bitsRemaining)); bitsRemaining += 3; } } //if we didn't end with a full byte if (arrayIndex != byteCount) { returnArray[arrayIndex] = curByte; } return returnArray; } public static string ToString(byte[] input) { if (input == null || input.Length == 0) { throw new ArgumentNullException("input"); } int charCount = (int)Math.Ceiling(input.Length / 5d) * 8; char[] returnArray = new char[charCount]; byte nextChar = 0, bitsRemaining = 5; int arrayIndex = 0; foreach (byte b in input) { nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining))); returnArray[arrayIndex++] = ValueToChar(nextChar); if (bitsRemaining < 4) { nextChar = (byte)((b >> (3 - bitsRemaining)) & 31); returnArray[arrayIndex++] = ValueToChar(nextChar); bitsRemaining += 5; } bitsRemaining -= 3; nextChar = (byte)((b << bitsRemaining) & 31); } //if we didn't end with a full char if (arrayIndex != charCount) { returnArray[arrayIndex++] = ValueToChar(nextChar); while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding } return new string(returnArray); } private static int CharToValue(char c) { var value = (int)c; //65-90 == uppercase letters if (value < 91 && value > 64) { return value - 65; } //50-55 == numbers 2-7 if (value < 56 && value > 49) { return value - 24; } //97-122 == lowercase letters if (value < 123 && value > 96) { return value - 97; } throw new ArgumentException("Character is not a Base32 character.", "c"); } private static char ValueToChar(byte b) { if (b < 26) { return (char)(b + 65); } if (b < 32) { return (char)(b + 24); } throw new ArgumentException("Byte is not a value Base32 value.", "b"); } [Fact] public void UserManagerWillUseTokenProviderInstanceOverDefaults() { var services = new ServiceCollection(); var provider = new ATokenProvider(); services.AddLogging() .AddIdentity(o => o.Tokens.ProviderMap.Add(TokenOptions.DefaultProvider, new TokenProviderDescriptor(typeof(ATokenProvider)) { ProviderInstance = provider })).AddUserStore().AddDefaultTokenProviders(); var manager = services.BuildServiceProvider().GetService>(); Assert.ThrowsAsync(() => manager.GenerateUserTokenAsync(new TestUser(), TokenOptions.DefaultProvider, "purpose")); } [Fact] public async Task TwoFactorStoreMethodsFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserTwoFactor); await Assert.ThrowsAsync(async () => await manager.GetTwoFactorEnabledAsync(null)); await Assert.ThrowsAsync(async () => await manager.SetTwoFactorEnabledAsync(null, true)); } [Fact] public async Task LockoutStoreMethodsFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserLockout); await Assert.ThrowsAsync(async () => await manager.GetLockoutEnabledAsync(null)); await Assert.ThrowsAsync(async () => await manager.SetLockoutEnabledAsync(null, true)); await Assert.ThrowsAsync(async () => await manager.AccessFailedAsync(null)); await Assert.ThrowsAsync(async () => await manager.IsLockedOutAsync(null)); await Assert.ThrowsAsync(async () => await manager.ResetAccessFailedCountAsync(null)); await Assert.ThrowsAsync(async () => await manager.GetAccessFailedCountAsync(null)); } [Fact] public async Task RoleMethodsFailWhenStoreNotImplemented() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserRole); await Assert.ThrowsAsync(async () => await manager.AddToRoleAsync(null, "bogus")); await Assert.ThrowsAsync(async () => await manager.AddToRolesAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.GetRolesAsync(null)); await Assert.ThrowsAsync(async () => await manager.RemoveFromRoleAsync(null, "bogus")); await Assert.ThrowsAsync(async () => await manager.RemoveFromRolesAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.IsInRoleAsync(null, "bogus")); } [Fact] public async Task AuthTokenMethodsFailWhenStoreNotImplemented() { var error = Resources.StoreNotIUserAuthenticationTokenStore; var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserAuthenticationTokens); await VerifyException(async () => await manager.GetAuthenticationTokenAsync(null, null, null), error); await VerifyException(async () => await manager.SetAuthenticationTokenAsync(null, null, null, null), error); await VerifyException(async () => await manager.RemoveAuthenticationTokenAsync(null, null, null), error); } [Fact] public async Task AuthenticatorMethodsFailWhenStoreNotImplemented() { var error = Resources.StoreNotIUserAuthenticatorKeyStore; var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserAuthenticatorKey); await VerifyException(async () => await manager.GetAuthenticatorKeyAsync(null), error); await VerifyException(async () => await manager.ResetAuthenticatorKeyAsync(null), error); } [Fact] public async Task RecoveryMethodsFailWhenStoreNotImplemented() { var error = Resources.StoreNotIUserTwoFactorRecoveryCodeStore; var manager = MockHelpers.TestUserManager(new NoopUserStore()); Assert.False(manager.SupportsUserTwoFactorRecoveryCodes); await VerifyException(async () => await manager.RedeemTwoFactorRecoveryCodeAsync(null, null), error); await VerifyException(async () => await manager.GenerateNewTwoFactorRecoveryCodesAsync(null, 10), error); } private async Task VerifyException(Func code, string expectedMessage) where TException : Exception { var error = await Assert.ThrowsAsync(code); Assert.Equal(expectedMessage, error.Message); } [Fact] public void DisposeAfterDisposeDoesNotThrow() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); manager.Dispose(); manager.Dispose(); } [Fact] public async Task PasswordValidatorBlocksCreate() { // TODO: Can switch to Mock eventually var manager = MockHelpers.TestUserManager(new EmptyStore()); manager.PasswordValidators.Clear(); manager.PasswordValidators.Add(new BadPasswordValidator()); IdentityResultAssert.IsFailure(await manager.CreateAsync(new TestUser(), "password"), BadPasswordValidator.ErrorMessage); } [Fact] public async Task ResetTokenCallNoopForTokenValueZero() { var user = new TestUser() { UserName = Guid.NewGuid().ToString() }; var store = new Mock>(); store.Setup(x => x.ResetAccessFailedCountAsync(user, It.IsAny())).Returns(() => { throw new Exception(); }); var manager = MockHelpers.TestUserManager(store.Object); IdentityResultAssert.IsSuccess(await manager.ResetAccessFailedCountAsync(user)); } [Fact] public async Task ManagerPublicNullChecks() { Assert.Throws("store", () => new UserManager(null, null, null, null, null, null, null, null, null)); var manager = MockHelpers.TestUserManager(new NotImplementedStore()); await Assert.ThrowsAsync("user", async () => await manager.CreateAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.CreateAsync(null, null)); await Assert.ThrowsAsync("password", async () => await manager.CreateAsync(new TestUser(), null)); await Assert.ThrowsAsync("user", async () => await manager.UpdateAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.DeleteAsync(null)); await Assert.ThrowsAsync("claim", async () => await manager.AddClaimAsync(null, null)); await Assert.ThrowsAsync("claim", async () => await manager.ReplaceClaimAsync(null, null, null)); await Assert.ThrowsAsync("claims", async () => await manager.AddClaimsAsync(null, null)); await Assert.ThrowsAsync("userName", async () => await manager.FindByNameAsync(null)); await Assert.ThrowsAsync("login", async () => await manager.AddLoginAsync(null, null)); await Assert.ThrowsAsync("loginProvider", async () => await manager.RemoveLoginAsync(null, null, null)); await Assert.ThrowsAsync("providerKey", async () => await manager.RemoveLoginAsync(null, "", null)); await Assert.ThrowsAsync("email", async () => await manager.FindByEmailAsync(null)); Assert.Throws("provider", () => manager.RegisterTokenProvider("whatever", null)); await Assert.ThrowsAsync("roles", async () => await manager.AddToRolesAsync(new TestUser(), null)); await Assert.ThrowsAsync("roles", async () => await manager.RemoveFromRolesAsync(new TestUser(), null)); } [Fact] public async Task MethodsFailWithUnknownUserTest() { var manager = MockHelpers.TestUserManager(new EmptyStore()); manager.RegisterTokenProvider("whatever", new NoOpTokenProvider()); await Assert.ThrowsAsync("user", async () => await manager.GetUserNameAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.SetUserNameAsync(null, "bogus")); await Assert.ThrowsAsync("user", async () => await manager.AddClaimAsync(null, new Claim("a", "b"))); await Assert.ThrowsAsync("user", async () => await manager.AddLoginAsync(null, new UserLoginInfo("", "", ""))); await Assert.ThrowsAsync("user", async () => await manager.AddPasswordAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.AddToRoleAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.AddToRolesAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.ChangePasswordAsync(null, null, null)); await Assert.ThrowsAsync("user", async () => await manager.GetClaimsAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.GetLoginsAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.GetRolesAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.IsInRoleAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.RemoveClaimAsync(null, new Claim("a", "b"))); await Assert.ThrowsAsync("user", async () => await manager.RemoveLoginAsync(null, "", "")); await Assert.ThrowsAsync("user", async () => await manager.RemovePasswordAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.RemoveFromRoleAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.RemoveFromRolesAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.ReplaceClaimAsync(null, new Claim("a", "b"), new Claim("a", "c"))); await Assert.ThrowsAsync("user", async () => await manager.UpdateSecurityStampAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.GetSecurityStampAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.HasPasswordAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.GeneratePasswordResetTokenAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.ResetPasswordAsync(null, null, null)); await Assert.ThrowsAsync("user", async () => await manager.IsEmailConfirmedAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.GenerateEmailConfirmationTokenAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.ConfirmEmailAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.GetEmailAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.SetEmailAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.IsPhoneNumberConfirmedAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.ChangePhoneNumberAsync(null, null, null)); await Assert.ThrowsAsync("user", async () => await manager.VerifyChangePhoneNumberTokenAsync(null, null, null)); await Assert.ThrowsAsync("user", async () => await manager.GetPhoneNumberAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.SetPhoneNumberAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.GetTwoFactorEnabledAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.SetTwoFactorEnabledAsync(null, true)); await Assert.ThrowsAsync("user", async () => await manager.GenerateTwoFactorTokenAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.VerifyTwoFactorTokenAsync(null, null, null)); await Assert.ThrowsAsync("user", async () => await manager.GetValidTwoFactorProvidersAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.VerifyUserTokenAsync(null, null, null, null)); await Assert.ThrowsAsync("user", async () => await manager.AccessFailedAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.ResetAccessFailedCountAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.GetAccessFailedCountAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.GetLockoutEnabledAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.SetLockoutEnabledAsync(null, false)); await Assert.ThrowsAsync("user", async () => await manager.SetLockoutEndDateAsync(null, DateTimeOffset.UtcNow)); await Assert.ThrowsAsync("user", async () => await manager.GetLockoutEndDateAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.IsLockedOutAsync(null)); } [Fact] public async Task MethodsThrowWhenDisposedTest() { var manager = MockHelpers.TestUserManager(new NoopUserStore()); manager.Dispose(); await Assert.ThrowsAsync(() => manager.AddClaimAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddClaimsAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddLoginAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddPasswordAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddToRoleAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddToRolesAsync(null, null)); await Assert.ThrowsAsync(() => manager.ChangePasswordAsync(null, null, null)); await Assert.ThrowsAsync(() => manager.GetClaimsAsync(null)); await Assert.ThrowsAsync(() => manager.GetLoginsAsync(null)); await Assert.ThrowsAsync(() => manager.GetRolesAsync(null)); await Assert.ThrowsAsync(() => manager.IsInRoleAsync(null, null)); await Assert.ThrowsAsync(() => manager.RemoveClaimAsync(null, null)); await Assert.ThrowsAsync(() => manager.RemoveClaimsAsync(null, null)); await Assert.ThrowsAsync(() => manager.RemoveLoginAsync(null, null, null)); await Assert.ThrowsAsync(() => manager.RemovePasswordAsync(null)); await Assert.ThrowsAsync(() => manager.RemoveFromRoleAsync(null, null)); await Assert.ThrowsAsync(() => manager.RemoveFromRolesAsync(null, null)); await Assert.ThrowsAsync(() => manager.FindByLoginAsync(null, null)); await Assert.ThrowsAsync(() => manager.FindByIdAsync(null)); await Assert.ThrowsAsync(() => manager.FindByNameAsync(null)); await Assert.ThrowsAsync(() => manager.CreateAsync(null)); await Assert.ThrowsAsync(() => manager.CreateAsync(null, null)); await Assert.ThrowsAsync(() => manager.UpdateAsync(null)); await Assert.ThrowsAsync(() => manager.DeleteAsync(null)); await Assert.ThrowsAsync(() => manager.ReplaceClaimAsync(null, null, null)); await Assert.ThrowsAsync(() => manager.UpdateSecurityStampAsync(null)); await Assert.ThrowsAsync(() => manager.GetSecurityStampAsync(null)); await Assert.ThrowsAsync(() => manager.GeneratePasswordResetTokenAsync(null)); await Assert.ThrowsAsync(() => manager.ResetPasswordAsync(null, null, null)); await Assert.ThrowsAsync(() => manager.GenerateEmailConfirmationTokenAsync(null)); await Assert.ThrowsAsync(() => manager.IsEmailConfirmedAsync(null)); await Assert.ThrowsAsync(() => manager.ConfirmEmailAsync(null, null)); } private class BadPasswordValidator : IPasswordValidator where TUser : class { public static readonly IdentityError ErrorMessage = new IdentityError { Description = "I'm Bad." }; public Task ValidateAsync(UserManager manager, TUser user, string password) { return Task.FromResult(IdentityResult.Failed(ErrorMessage)); } } private class EmptyStore : IUserPasswordStore, IUserClaimStore, IUserLoginStore, IUserEmailStore, IUserPhoneNumberStore, IUserLockoutStore, IUserTwoFactorStore, IUserRoleStore, IUserSecurityStampStore { public Task> GetClaimsAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult>(new List()); } public Task AddClaimsAsync(TestUser user, IEnumerable claim, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task ReplaceClaimAsync(TestUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task RemoveClaimsAsync(TestUser user, IEnumerable claim, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task SetEmailAsync(TestUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task GetEmailAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(""); } public Task GetEmailConfirmedAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(false); } public Task SetEmailConfirmedAsync(TestUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(null); } public Task GetLockoutEndDateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(DateTimeOffset.MinValue); } public Task SetLockoutEndDateAsync(TestUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task IncrementAccessFailedCountAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task ResetAccessFailedCountAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task GetAccessFailedCountAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task GetLockoutEnabledAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(false); } public Task SetLockoutEnabledAsync(TestUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task AddLoginAsync(TestUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task RemoveLoginAsync(TestUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task> GetLoginsAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult>(new List()); } public Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(null); } public void Dispose() { } public Task SetUserNameAsync(TestUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task CreateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(IdentityResult.Success); } public Task UpdateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(IdentityResult.Success); } public Task DeleteAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(IdentityResult.Success); } public Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(null); } public Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(null); } public Task SetPasswordHashAsync(TestUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task GetPasswordHashAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(null); } public Task HasPasswordAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(false); } public Task SetPhoneNumberAsync(TestUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task GetPhoneNumberAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(""); } public Task GetPhoneNumberConfirmedAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(false); } public Task SetPhoneNumberConfirmedAsync(TestUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task AddToRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task RemoveFromRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task> GetRolesAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult>(new List()); } public Task IsInRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(false); } public Task SetSecurityStampAsync(TestUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task GetSecurityStampAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(""); } public Task SetTwoFactorEnabledAsync(TestUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task GetTwoFactorEnabledAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(false); } public Task GetUserIdAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(null); } public Task GetUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(null); } public Task GetNormalizedUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(null); } public Task SetNormalizedUserNameAsync(TestUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } public Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult>(new List()); } public Task> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult>(new List()); } public Task GetNormalizedEmailAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(""); } public Task SetNormalizedEmailAsync(TestUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(0); } } private class NoOpTokenProvider : IUserTwoFactorTokenProvider { public string Name { get; } = "Noop"; public Task GenerateAsync(string purpose, UserManager manager, TestUser user) { return Task.FromResult("Test"); } public Task ValidateAsync(string purpose, string token, UserManager manager, TestUser user) { return Task.FromResult(true); } public Task CanGenerateTwoFactorTokenAsync(UserManager manager, TestUser user) { return Task.FromResult(true); } } private class NotImplementedStore : IUserPasswordStore, IUserClaimStore, IUserLoginStore, IUserRoleStore, IUserEmailStore, IUserPhoneNumberStore, IUserLockoutStore, IUserTwoFactorStore { public Task> GetClaimsAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task AddClaimsAsync(TestUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task ReplaceClaimAsync(TestUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task RemoveClaimsAsync(TestUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetEmailAsync(TestUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetEmailAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetEmailConfirmedAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetEmailConfirmedAsync(TestUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetLockoutEndDateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetLockoutEndDateAsync(TestUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task IncrementAccessFailedCountAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task ResetAccessFailedCountAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetAccessFailedCountAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetLockoutEnabledAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetLockoutEnabledAsync(TestUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task AddLoginAsync(TestUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task RemoveLoginAsync(TestUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task> GetLoginsAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public void Dispose() { throw new NotImplementedException(); } public Task GetUserIdAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetUserNameAsync(TestUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetPasswordHashAsync(TestUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetPasswordHashAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task HasPasswordAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetPhoneNumberAsync(TestUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetPhoneNumberAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetPhoneNumberConfirmedAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetPhoneNumberConfirmedAsync(TestUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetTwoFactorEnabledAsync(TestUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetTwoFactorEnabledAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task AddToRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task RemoveFromRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task> GetRolesAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task IsInRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task GetNormalizedUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetNormalizedUserNameAsync(TestUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } Task IUserStore.CreateAsync(TestUser user, CancellationToken cancellationToken) { throw new NotImplementedException(); } Task IUserStore.UpdateAsync(TestUser user, CancellationToken cancellationToken) { throw new NotImplementedException(); } Task IUserStore.DeleteAsync(TestUser user, CancellationToken cancellationToken) { throw new NotImplementedException(); } public Task GetNormalizedEmailAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } public Task SetNormalizedEmailAsync(TestUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } } [Fact] public async Task CanCustomizeUserValidatorErrors() { var services = new ServiceCollection(); var store = new Mock>(); var describer = new TestErrorDescriber(); services.AddSingleton(describer) .AddSingleton>(store.Object) .AddIdentity(); services.AddLogging(); services.AddSingleton(); var manager = services.BuildServiceProvider().GetRequiredService>(); manager.Options.User.RequireUniqueEmail = true; var user = new TestUser() { UserName = "dupeEmail", Email = "dupe@email.com" }; var user2 = new TestUser() { UserName = "dupeEmail2", Email = "dupe@email.com" }; store.Setup(s => s.FindByEmailAsync("DUPE@EMAIL.COM", CancellationToken.None)) .Returns(Task.FromResult(user2)) .Verifiable(); store.Setup(s => s.GetUserIdAsync(user2, CancellationToken.None)) .Returns(Task.FromResult(user2.Id)) .Verifiable(); store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)) .Returns(Task.FromResult(user.UserName)) .Verifiable(); store.Setup(s => s.GetEmailAsync(user, CancellationToken.None)) .Returns(Task.FromResult(user.Email)) .Verifiable(); Assert.Same(describer, manager.ErrorDescriber); IdentityResultAssert.IsFailure(await manager.CreateAsync(user), describer.DuplicateEmail(user.Email)); store.VerifyAll(); } public class TestErrorDescriber : IdentityErrorDescriber { public static string Code = "Error"; public static string FormatError = "FormatError {0}"; public override IdentityError DuplicateEmail(string email) { return new IdentityError { Code = Code, Description = string.Format(FormatError, email) }; } } } }