diff --git a/sample/MongoIdentitySample.Mvc/MongoIdentitySample.Mvc.csproj b/sample/MongoIdentitySample.Mvc/MongoIdentitySample.Mvc.csproj index ff6636c..d682972 100644 --- a/sample/MongoIdentitySample.Mvc/MongoIdentitySample.Mvc.csproj +++ b/sample/MongoIdentitySample.Mvc/MongoIdentitySample.Mvc.csproj @@ -1,27 +1,25 @@ - + - netcoreapp2.2 - $(PackageTargetFallback);portable-net45+win8+wp8+wpa81; + netcoreapp3.1 aspnet-MongoIdentitySample.Mvc-95B15D82-54F6-4001-B4B0-6ADF4B1BB00E - - + - - - + + + - - - + + + @@ -31,4 +29,14 @@ + + + + + + + Always + + + diff --git a/sample/MongoIdentitySample.Mvc/Startup.cs b/sample/MongoIdentitySample.Mvc/Startup.cs index 7d71e23..6583063 100644 --- a/sample/MongoIdentitySample.Mvc/Startup.cs +++ b/sample/MongoIdentitySample.Mvc/Startup.cs @@ -53,10 +53,10 @@ namespace MongoIdentitySample.Mvc } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IHostingEnvironment env) //, ILoggerFactory loggerFactory) { - loggerFactory.AddConsole(Configuration.GetSection("Logging")); - loggerFactory.AddDebug(); + //loggerFactory.AddConsole(Configuration.GetSection("Logging")); + //loggerFactory.AddDebug(); if (env.IsDevelopment()) { @@ -68,17 +68,23 @@ namespace MongoIdentitySample.Mvc app.UseExceptionHandler("/Home/Error"); } + app.UseRouting(); app.UseStaticFiles(); - - app.UseIdentity(); - + + app.UseAuthentication(); + app.UseAuthorization(); // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715 - app.UseMvc(routes => + //app.UseMvc(routes => + //{ + // routes.MapRoute( + // name: "default", + // template: "{controller=Home}/{action=Index}/{id?}"); + //}); + + app.UseEndpoints(endpoints => { - routes.MapRoute( - name: "default", - template: "{controller=Home}/{action=Index}/{id?}"); + endpoints.MapDefaultControllerRoute(); }); } } diff --git a/src/AspNetCore.Identity.MongoDbCore.csproj b/src/AspNetCore.Identity.MongoDbCore.csproj index 0ff1987..d416077 100644 --- a/src/AspNetCore.Identity.MongoDbCore.csproj +++ b/src/AspNetCore.Identity.MongoDbCore.csproj @@ -1,22 +1,22 @@  - netcoreapp2.0;netstandard2.0 + netcoreapp3.1;netstandard2.1 - bin\Release\netstandard2.0\AspNetCore.Identity.MongoDbCore.xml + bin\Release\netstandard2.1\AspNetCore.Identity.MongoDbCore.xml - bin\Debug\netstandard2.0\AspNetCore.Identity.MongoDbCore.xml + bin\Debug\netstandard2.1\AspNetCore.Identity.MongoDbCore.xml - - - + + + diff --git a/src/MongoUserStore.cs b/src/MongoUserStore.cs index 9cfbeb4..af3c69c 100644 --- a/src/MongoUserStore.cs +++ b/src/MongoUserStore.cs @@ -211,10 +211,13 @@ namespace AspNetCore.Identity.MongoDbCore var updateRes = await collection.ReplaceOneAsync(x => x.Id.Equals(user.Id) && x.ConcurrencyStamp.Equals(oldStamp), user); + + if (updateRes.ModifiedCount == 0) { return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure()); } + return IdentityResult.Success; } diff --git a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.MongoDbCore.IntegrationTests.csproj b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.MongoDbCore.IntegrationTests.csproj index a099d71..44f31f6 100644 --- a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.MongoDbCore.IntegrationTests.csproj +++ b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.MongoDbCore.IntegrationTests.csproj @@ -1,30 +1,36 @@ - netcoreapp2.0 + netcoreapp3.1 false - - - + + - - - - + + + + - - - - - - - + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/IdentityBuilderTest.cs b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/IdentityBuilderTest.cs index 0c3b8c7..9ed9e72 100644 --- a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/IdentityBuilderTest.cs +++ b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/IdentityBuilderTest.cs @@ -300,7 +300,7 @@ namespace Microsoft.AspNetCore.Identity.Test private class MySignInManager : SignInManager { - public MySignInManager(UserManager manager, IHttpContextAccessor context, IUserClaimsPrincipalFactory claimsFactory) : base(manager, context, claimsFactory, null, null, null) { } + public MySignInManager(UserManager manager, IHttpContextAccessor context, IUserClaimsPrincipalFactory claimsFactory) : base(manager, context, claimsFactory, null, null, null, null) { } } private class MyUserManager : UserManager diff --git a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/SecurityStampValidatorTest.cs b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/SecurityStampValidatorTest.cs index cbbd961..907a704 100644 --- a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/SecurityStampValidatorTest.cs +++ b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/SecurityStampValidatorTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Text; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; @@ -9,9 +10,11 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using Xunit; +using AspNetCore.Identity.MongoDbCore.IntegrationTests; namespace Microsoft.AspNetCore.Identity.Test { @@ -71,150 +74,263 @@ namespace Microsoft.AspNetCore.Identity.Test [InlineData(false)] public async Task OnValidatePrincipalTestSuccess(bool isPersistent) { - var user = new TestUser("test"); - var userManager = MockHelpers.MockUserManager(); - var claimsManager = new Mock>(); - var identityOptions = new Mock>(); - identityOptions.Setup(a => a.Value).Returns(new IdentityOptions()); - var options = new Mock>(); - options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); - var httpContext = new Mock(); + var user = new TestUser { UserName = "test" }; + + var manager = SetupUserManager(user); + manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); + manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable(); + + var context = new Mock(); + var contextAccessor = new Mock(); - contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); + contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); + + var roleManager = MockHelpers.MockRoleManager(); + + var options = new Mock>(); + options.Setup(a => a.Value).Returns(new IdentityOptions()); + + var securityStampOptions = new Mock>(); + securityStampOptions.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); + + var claimsFactory = new UserClaimsPrincipalFactory(manager.Object, roleManager.Object, options.Object); + + var loggerFactory = new MockLoggerFactory(); + var logger = loggerFactory.CreateLogger>(); + + var helper = new Mock>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock().Object, new Mock>().Object); + var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1), IsPersistent = isPersistent }; + var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); var principal = new ClaimsPrincipal(id); - var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1), IsPersistent = isPersistent }; - var signInManager = new Mock>(userManager.Object, - contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock().Object); - signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(user).Verifiable(); - signInManager.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable(); + helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(user).Verifiable(); + helper.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable(); + + var logFactory = new MockLoggerFactory(); var services = new ServiceCollection(); + services.AddSingleton(loggerFactory); services.AddSingleton(options.Object); - services.AddSingleton(signInManager.Object); - services.AddSingleton(new SecurityStampValidator(options.Object, signInManager.Object, new SystemClock())); - httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); + services.AddSingleton(helper.Object); + services.AddSingleton(new SecurityStampValidator(securityStampOptions.Object, helper.Object, new SystemClock(), loggerFactory)); + + context.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); + contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); var ticket = new AuthenticationTicket(principal, properties, IdentityConstants.ApplicationScheme); - var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); - Assert.NotNull(context.Properties); - Assert.NotNull(context.Options); - Assert.NotNull(context.Principal); + var cookieContext = new CookieValidatePrincipalContext(context.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); + Assert.NotNull(cookieContext.Properties); + Assert.NotNull(cookieContext.Options); + Assert.NotNull(cookieContext.Principal); await - SecurityStampValidator.ValidatePrincipalAsync(context); - Assert.NotNull(context.Principal); - signInManager.VerifyAll(); + SecurityStampValidator.ValidatePrincipalAsync(cookieContext); + Assert.NotNull(cookieContext.Principal); + helper.VerifyAll(); + } + + private static Mock> SetupUserManager(TestUser user) + { + var manager = MockHelpers.MockUserManager(); + manager.Setup(m => m.FindByNameAsync(user.UserName)).ReturnsAsync(user); + manager.Setup(m => m.FindByIdAsync(user.Id)).ReturnsAsync(user); + manager.Setup(m => m.GetUserIdAsync(user)).ReturnsAsync(user.Id.ToString()); + manager.Setup(m => m.GetUserNameAsync(user)).ReturnsAsync(user.UserName); + return manager; } [Fact] public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails() { - var user = new TestUser("test"); - var userManager = MockHelpers.MockUserManager(); - var claimsManager = new Mock>(); - var identityOptions = new Mock>(); - identityOptions.Setup(a => a.Value).Returns(new IdentityOptions()); - var options = new Mock>(); - options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); - var httpContext = new Mock(); + var user = new TestUser { UserName = "test" }; + + var manager = SetupUserManager(user); + manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); + manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable(); + + var context = new Mock(); + var contextAccessor = new Mock(); - contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); - var signInManager = new Mock>(userManager.Object, - contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock().Object); - signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(default(TestUser)).Verifiable(); - var services = new ServiceCollection(); - services.AddSingleton(options.Object); - services.AddSingleton(signInManager.Object); - services.AddSingleton(new SecurityStampValidator(options.Object, signInManager.Object, new SystemClock())); - httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); + contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); + + var roleManager = MockHelpers.MockRoleManager(); + + var options = new Mock>(); + options.Setup(a => a.Value).Returns(new IdentityOptions()); + + var securityStampOptions = new Mock>(); + securityStampOptions.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); + + var claimsFactory = new UserClaimsPrincipalFactory(manager.Object, roleManager.Object, options.Object); + var logStore = new StringBuilder(); + var loggerFactory = new MockLoggerFactory(); + var logger = loggerFactory.CreateLogger>(); + + var helper = new Mock>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock().Object, new Mock>().Object); + var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) }; + var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); + var principal = new ClaimsPrincipal(id); + //because the request fails the create user principal is never called and therefore not verifiable + //helper.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable(); + //helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(user).Verifiable(); + helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(default(TestUser)).Verifiable(); + + var services = new ServiceCollection(); + + services.AddSingleton(options.Object); + services.AddSingleton(helper.Object); + services.AddSingleton(loggerFactory); + services.AddSingleton(new SecurityStampValidator(securityStampOptions.Object, helper.Object, new SystemClock(), loggerFactory)); + + context.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); + contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); var ticket = new AuthenticationTicket(new ClaimsPrincipal(id), new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) }, IdentityConstants.ApplicationScheme); - var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); - Assert.NotNull(context.Properties); - Assert.NotNull(context.Options); - Assert.NotNull(context.Principal); - await SecurityStampValidator.ValidatePrincipalAsync(context); - Assert.Null(context.Principal); - signInManager.VerifyAll(); + + var cookieContext = new CookieValidatePrincipalContext(context.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); + + Assert.NotNull(cookieContext.Properties); + Assert.NotNull(cookieContext.Options); + Assert.NotNull(cookieContext.Principal); + + await SecurityStampValidator.ValidatePrincipalAsync(cookieContext); + + Assert.Null(cookieContext.Principal); + + helper.VerifyAll(); } [Fact] public async Task OnValidateIdentityRejectsWhenNoIssuedUtc() { - var user = new TestUser("test"); - var httpContext = new Mock(); - var userManager = MockHelpers.MockUserManager(); - var identityOptions = new Mock>(); - identityOptions.Setup(a => a.Value).Returns(new IdentityOptions()); - var claimsManager = new Mock>(); - var options = new Mock>(); - options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); + var user = new TestUser { UserName = "test" }; + + var manager = SetupUserManager(user); + manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); + manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable(); + + var context = new Mock(); + var contextAccessor = new Mock(); - contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); - var signInManager = new Mock>(userManager.Object, - contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock().Object); - signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(default(TestUser)).Verifiable(); - var services = new ServiceCollection(); - services.AddSingleton(options.Object); - services.AddSingleton(signInManager.Object); - services.AddSingleton(new SecurityStampValidator(options.Object, signInManager.Object, new SystemClock())); - httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); + contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); + + var roleManager = MockHelpers.MockRoleManager(); + + var options = new Mock>(); + options.Setup(a => a.Value).Returns(new IdentityOptions()); + + var securityStampOptions = new Mock>(); + securityStampOptions.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); + + var claimsFactory = new UserClaimsPrincipalFactory(manager.Object, roleManager.Object, options.Object); + var logStore = new StringBuilder(); + var loggerFactory = new MockLoggerFactory(); + var logger = loggerFactory.CreateLogger>(); + + var helper = new Mock>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock().Object, new Mock>().Object); + var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) }; + var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); + var principal = new ClaimsPrincipal(id); + //because the request fails the create user principal is never called and therefore not verifiable + //helper.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable(); + //helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(user).Verifiable(); + helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(default(TestUser)).Verifiable(); + var services = new ServiceCollection(); + + services.AddSingleton(options.Object); + services.AddSingleton(helper.Object); + services.AddSingleton(loggerFactory); + services.AddSingleton(new SecurityStampValidator(securityStampOptions.Object, helper.Object, new SystemClock(), loggerFactory)); + + context.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); + contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); + + // testing the ticket UTC setting, in this case lack of setting var ticket = new AuthenticationTicket(new ClaimsPrincipal(id), new AuthenticationProperties(), IdentityConstants.ApplicationScheme); - var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); - Assert.NotNull(context.Properties); - Assert.NotNull(context.Options); - Assert.NotNull(context.Principal); - await SecurityStampValidator.ValidatePrincipalAsync(context); - Assert.Null(context.Principal); - signInManager.VerifyAll(); + + var cookieContext = new CookieValidatePrincipalContext(context.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); + + Assert.NotNull(cookieContext.Properties); + Assert.NotNull(cookieContext.Options); + Assert.NotNull(cookieContext.Principal); + + await SecurityStampValidator.ValidatePrincipalAsync(cookieContext); + + Assert.Null(cookieContext.Principal); + + helper.VerifyAll(); } [Fact] public async Task OnValidateIdentityDoesNotRejectsWhenNotExpired() { - var user = new TestUser("test"); - var httpContext = new Mock(); - var userManager = MockHelpers.MockUserManager(); - var identityOptions = new Mock>(); - identityOptions.Setup(a => a.Value).Returns(new IdentityOptions()); - var claimsManager = new Mock>(); - var options = new Mock>(); - options.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.FromDays(1) }); + var user = new TestUser { UserName = "test" }; + + var manager = SetupUserManager(user); + manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); + manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(true).Verifiable(); + + var context = new Mock(); + var contextAccessor = new Mock(); - contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); - var signInManager = new Mock>(userManager.Object, - contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock().Object); - signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).Throws(new Exception("Shouldn't be called")); - signInManager.Setup(s => s.SignInAsync(user, false, null)).Throws(new Exception("Shouldn't be called")); - var services = new ServiceCollection(); - services.AddSingleton(options.Object); - services.AddSingleton(signInManager.Object); - services.AddSingleton(new SecurityStampValidator(options.Object, signInManager.Object, new SystemClock())); - httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); + contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); + + var roleManager = MockHelpers.MockRoleManager(); + + var options = new Mock>(); + options.Setup(a => a.Value).Returns(new IdentityOptions()); + + var securityStampOptions = new Mock>(); + securityStampOptions.Setup(a => a.Value).Returns(new SecurityStampValidatorOptions { ValidationInterval = TimeSpan.Zero }); + + var claimsFactory = new UserClaimsPrincipalFactory(manager.Object, roleManager.Object, options.Object); + var logStore = new StringBuilder(); + var loggerFactory = new MockLoggerFactory(); + var logger = loggerFactory.CreateLogger>(); + + var helper = new Mock>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock().Object, new Mock>().Object); + var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) }; + var id = new ClaimsIdentity(IdentityConstants.ApplicationScheme); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); + var principal = new ClaimsPrincipal(id); + //because the request fails the create user principal is never called and therefore not verifiable + //helper.Setup(s => s.CreateUserPrincipalAsync(user)).ReturnsAsync(principal).Verifiable(); + //helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(user).Verifiable(); + //helper.Setup(s => s.ValidateSecurityStampAsync(It.IsAny())).ReturnsAsync(default(TestUser)).Verifiable(); + helper.Setup(s => s.SignInAsync(user, false, null)).Throws(new Exception("Shouldn't be called")); + + var services = new ServiceCollection(); + + services.AddSingleton(options.Object); + services.AddSingleton(helper.Object); + services.AddSingleton(loggerFactory); + services.AddSingleton(new SecurityStampValidator(securityStampOptions.Object, helper.Object, new SystemClock(), loggerFactory)); + + context.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider()); + contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); var ticket = new AuthenticationTicket(new ClaimsPrincipal(id), new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow }, IdentityConstants.ApplicationScheme); - var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); - Assert.NotNull(context.Properties); - Assert.NotNull(context.Options); - Assert.NotNull(context.Principal); - await SecurityStampValidator.ValidatePrincipalAsync(context); - Assert.NotNull(context.Principal); + + var cookieContext = new CookieValidatePrincipalContext(context.Object, new AuthenticationSchemeBuilder(IdentityConstants.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), new CookieAuthenticationOptions(), ticket); + Assert.NotNull(cookieContext.Properties); + Assert.NotNull(cookieContext.Options); + Assert.NotNull(cookieContext.Principal); + await SecurityStampValidator.ValidatePrincipalAsync(cookieContext); + Assert.NotNull(cookieContext.Principal); } } } \ No newline at end of file diff --git a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/SignInManagerTest.cs b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/SignInManagerTest.cs index e8f942c..895c4ca 100644 --- a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/SignInManagerTest.cs +++ b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/AspNetCore.Identity.Test/SignInManagerTest.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; +using AspNetCore.Identity.MongoDbCore.IntegrationTests; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -68,13 +69,13 @@ namespace Microsoft.AspNetCore.Identity.Test [Fact] public void ConstructorNullChecks() { - Assert.Throws("userManager", () => new SignInManager(null, null, null, null, null, null)); + Assert.Throws("userManager", () => new SignInManager(null, null, null, null, null, null, null)); var userManager = MockHelpers.MockUserManager().Object; - Assert.Throws("contextAccessor", () => new SignInManager(userManager, null, null, null, null, null)); + Assert.Throws("contextAccessor", () => new SignInManager(userManager, null, null, null, null, null, null)); var contextAccessor = new Mock(); var context = new Mock(); contextAccessor.Setup(a => a.HttpContext).Returns(context.Object); - Assert.Throws("claimsFactory", () => new SignInManager(userManager, contextAccessor.Object, null, null, null, null)); + Assert.Throws("claimsFactory", () => new SignInManager(userManager, contextAccessor.Object, null, null, null, null, null)); } //[Fact] @@ -122,9 +123,9 @@ namespace Microsoft.AspNetCore.Identity.Test var options = new Mock>(); options.Setup(a => a.Value).Returns(identityOptions); var claimsFactory = new UserClaimsPrincipalFactory(manager.Object, roleManager.Object, options.Object); - var logStore = new StringBuilder(); - var logger = MockHelpers.MockILogger>(logStore); - var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger.Object, new Mock().Object); + var loggerFactory = new MockLoggerFactory(); + var logger = loggerFactory.CreateLogger>(); + var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock().Object, new Mock>().Object); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); @@ -132,7 +133,7 @@ namespace Microsoft.AspNetCore.Identity.Test // Assert Assert.False(result.Succeeded); Assert.True(result.IsLockedOut); - Assert.Contains($"User {user.Id} is currently locked out.", logStore.ToString()); + Assert.Contains($"User {user.Id} is currently locked out.", loggerFactory.LogStore.ToString()); manager.Verify(); } @@ -153,9 +154,9 @@ namespace Microsoft.AspNetCore.Identity.Test var options = new Mock>(); options.Setup(a => a.Value).Returns(identityOptions); var claimsFactory = new UserClaimsPrincipalFactory(manager.Object, roleManager.Object, options.Object); - var logStore = new StringBuilder(); - var logger = MockHelpers.MockILogger>(logStore); - var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger.Object, new Mock().Object); + var loggerFactory = new MockLoggerFactory(); + var logger = loggerFactory.CreateLogger>(); + var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory, options.Object, logger, new Mock().Object, new Mock>().Object); // Act var result = await helper.CheckPasswordSignInAsync(user, "bogus", false); @@ -163,7 +164,7 @@ namespace Microsoft.AspNetCore.Identity.Test // Assert Assert.False(result.Succeeded); Assert.True(result.IsLockedOut); - Assert.Contains($"User {user.Id} is currently locked out.", logStore.ToString()); + Assert.Contains($"User {user.Id} is currently locked out.", loggerFactory.LogStore.ToString()); manager.Verify(); } @@ -177,7 +178,7 @@ namespace Microsoft.AspNetCore.Identity.Test return manager; } - private static SignInManager SetupSignInManager(UserManager manager, HttpContext context, StringBuilder logStore = null, IdentityOptions identityOptions = null) + private static SignInManager SetupSignInManager(UserManager manager, HttpContext context, MockLoggerFactory factory, IdentityOptions identityOptions = null) { var contextAccessor = new Mock(); contextAccessor.Setup(a => a.HttpContext).Returns(context); @@ -186,8 +187,20 @@ namespace Microsoft.AspNetCore.Identity.Test var options = new Mock>(); options.Setup(a => a.Value).Returns(identityOptions); var claimsFactory = new UserClaimsPrincipalFactory(manager, roleManager.Object, options.Object); - var sm = new SignInManager(manager, contextAccessor.Object, claimsFactory, options.Object, null, new Mock().Object); - sm.Logger = MockHelpers.MockILogger>(logStore ?? new StringBuilder()).Object; + var sm = new SignInManager(manager, contextAccessor.Object, claimsFactory, options.Object,factory.CreateLogger>(), new Mock().Object, new Mock>().Object); + return sm; + } + + private static Mock> MockSignInManager(UserManager manager, HttpContext context, MockLoggerFactory factory, IdentityOptions identityOptions = null) + { + var contextAccessor = new Mock(); + contextAccessor.Setup(a => a.HttpContext).Returns(context); + var roleManager = MockHelpers.MockRoleManager(); + identityOptions = identityOptions ?? new IdentityOptions(); + var options = new Mock>(); + options.Setup(a => a.Value).Returns(identityOptions); + var claimsFactory = new UserClaimsPrincipalFactory(manager, roleManager.Object, options.Object); + var sm = new Mock>(manager, contextAccessor.Object, claimsFactory, options.Object, factory.CreateLogger>(), new Mock().Object, new Mock>().Object); return sm; } @@ -206,7 +219,9 @@ namespace Microsoft.AspNetCore.Identity.Test var context = new DefaultHttpContext(); var auth = MockAuth(context); SetupSignIn(context, auth, user.Id, isPersistent); - var helper = SetupSignInManager(manager.Object, context); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); @@ -230,8 +245,8 @@ namespace Microsoft.AspNetCore.Identity.Test var context = new DefaultHttpContext(); var auth = MockAuth(context); SetupSignIn(context, auth, user.Id, false); - var helper = SetupSignInManager(manager.Object, context); - + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); @@ -256,8 +271,8 @@ namespace Microsoft.AspNetCore.Identity.Test var context = new DefaultHttpContext(); var auth = MockAuth(context); SetupSignIn(context, auth); - var helper = SetupSignInManager(manager.Object, context); - + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); @@ -288,7 +303,10 @@ namespace Microsoft.AspNetCore.Identity.Test manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable(); manager.Setup(m => m.GetValidTwoFactorProvidersAsync(user)).ReturnsAsync(new string[1] { "Fake" }).Verifiable(); var context = new DefaultHttpContext(); - var helper = SetupSignInManager(manager.Object, context); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory); + var auth = MockAuth(context); auth.Setup(a => a.SignInAsync(context, IdentityConstants.TwoFactorUserIdScheme, It.Is(id => id.FindFirstValue(ClaimTypes.Name) == user.Id), @@ -326,7 +344,9 @@ namespace Microsoft.AspNetCore.Identity.Test } var context = new DefaultHttpContext(); var auth = MockAuth(context); - var helper = SetupSignInManager(manager.Object, context); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory); if (bypass) { @@ -473,7 +493,10 @@ namespace Microsoft.AspNetCore.Identity.Test var context = new DefaultHttpContext(); var auth = MockAuth(context); - var helper = SetupSignInManager(manager.Object, context); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory); + SetupSignIn(context, auth, user.Id, isPersistent, loginProvider); // Act @@ -493,6 +516,7 @@ namespace Microsoft.AspNetCore.Identity.Test // Setup var user = new TestUser { UserName = "Foo" }; var context = new DefaultHttpContext(); + var services = new ServiceCollection(); var auth = MockAuth(context); var loginProvider = "loginprovider"; var id = new ClaimsIdentity(); @@ -503,25 +527,26 @@ namespace Microsoft.AspNetCore.Identity.Test // REVIEW: auth changes we lost the ability to mock is persistent //var properties = new AuthenticationProperties { IsPersistent = isPersistent }; var authResult = AuthenticateResult.NoResult(); - auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.ApplicationScheme)) - .Returns(Task.FromResult(authResult)).Verifiable(); + + auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.ApplicationScheme)).Returns(Task.FromResult(authResult)).Verifiable(); + var manager = SetupUserManager(user); - var signInManager = new Mock>(manager.Object, - new HttpContextAccessor { HttpContext = context }, - new Mock>().Object, - null, null, new Mock().Object) - { CallBase = true }; - //signInManager.Setup(s => s.SignInAsync(user, It.Is(p => p.IsPersistent == isPersistent), - //externalLogin? loginProvider : null)).Returns(Task.FromResult(0)).Verifiable(); - signInManager.Setup(s => s.SignInAsync(user, It.IsAny(), null)).Returns(Task.FromResult(0)).Verifiable(); - signInManager.Object.Context = context; - // Act - await signInManager.Object.RefreshSignInAsync(user); + using (MockLoggerFactory loggerFactory = new MockLoggerFactory()) + { + var signInManager = MockSignInManager(manager.Object, context, loggerFactory, null); - // Assert - auth.Verify(); - signInManager.Verify(); + signInManager.CallBase = true; // need this magic! + + signInManager.Setup(s => s.SignInWithClaimsAsync(user, It.IsAny(), It.IsAny>())).Returns(Task.FromResult(0)).Verifiable(); + + // Act + await signInManager.Object.RefreshSignInAsync(user); + + // Assert + auth.Verify(); + signInManager.Verify(); + } } //[Theory] @@ -606,7 +631,10 @@ namespace Microsoft.AspNetCore.Identity.Test var manager = SetupUserManager(user); var context = new DefaultHttpContext(); var auth = MockAuth(context); - var helper = SetupSignInManager(manager.Object, context); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory); + auth.Setup(a => a.SignInAsync( context, IdentityConstants.TwoFactorRememberMeScheme, @@ -644,9 +672,12 @@ namespace Microsoft.AspNetCore.Identity.Test SetupSignIn(context, auth); var id = new ClaimsIdentity(IdentityConstants.TwoFactorRememberMeScheme); id.AddClaim(new Claim(ClaimTypes.Name, user.Id)); - auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorRememberMeScheme)) + + auth.Setup(a => a.AuthenticateAsync(It.IsAny(), IdentityConstants.TwoFactorRememberMeScheme)) .ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), null, IdentityConstants.TwoFactorRememberMeScheme))).Verifiable(); - var helper = SetupSignInManager(manager.Object, context); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); @@ -664,6 +695,15 @@ namespace Microsoft.AspNetCore.Identity.Test return auth; } + private Mock MockAuth(ServiceCollection services) + { + var auth = new Mock(); + + services.AddSingleton(auth.Object); + + return auth; + } + [Fact] public async Task SignOutCallsContextResponseSignOut() { @@ -671,10 +711,11 @@ namespace Microsoft.AspNetCore.Identity.Test var manager = MockHelpers.TestUserManager(); var context = new DefaultHttpContext(); var auth = MockAuth(context); + var loggerFactory = new MockLoggerFactory(); auth.Setup(a => a.SignOutAsync(context, IdentityConstants.ApplicationScheme, It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); auth.Setup(a => a.SignOutAsync(context, IdentityConstants.TwoFactorUserIdScheme, It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); auth.Setup(a => a.SignOutAsync(context, IdentityConstants.ExternalScheme, It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); - var helper = SetupSignInManager(manager, context, null, manager.Options); + var helper = SetupSignInManager(manager, context, loggerFactory, manager.Options); // Act await helper.SignOutAsync(); @@ -693,8 +734,10 @@ namespace Microsoft.AspNetCore.Identity.Test manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable(); var context = new Mock(); - var logStore = new StringBuilder(); - var helper = SetupSignInManager(manager.Object, context.Object, logStore); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context.Object, loggerFactory); + // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); var checkResult = await helper.CheckPasswordSignInAsync(user, "bogus", false); @@ -702,7 +745,7 @@ namespace Microsoft.AspNetCore.Identity.Test // Assert Assert.False(result.Succeeded); Assert.False(checkResult.Succeeded); - Assert.Contains($"User {user.Id} failed to provide the correct password.", logStore.ToString()); + Assert.Contains($"User {user.Id} failed to provide the correct password.", loggerFactory.LogStore.ToString()); manager.Verify(); context.Verify(); } @@ -714,7 +757,9 @@ namespace Microsoft.AspNetCore.Identity.Test var manager = MockHelpers.MockUserManager(); manager.Setup(m => m.FindByNameAsync("bogus")).ReturnsAsync(default(TestUser)).Verifiable(); var context = new Mock(); - var helper = SetupSignInManager(manager.Object, context.Object); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context.Object, loggerFactory); // Act var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false); @@ -741,7 +786,9 @@ namespace Microsoft.AspNetCore.Identity.Test manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout)); manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable(); var context = new Mock(); - var helper = SetupSignInManager(manager.Object, context.Object); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context.Object, loggerFactory); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true); @@ -768,7 +815,9 @@ namespace Microsoft.AspNetCore.Identity.Test manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout)); manager.Setup(m => m.CheckPasswordAsync(user, "bogus")).ReturnsAsync(false).Verifiable(); var context = new Mock(); - var helper = SetupSignInManager(manager.Object, context.Object); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context.Object, loggerFactory); // Act var result = await helper.CheckPasswordSignInAsync(user, "bogus", true); @@ -802,8 +851,9 @@ namespace Microsoft.AspNetCore.Identity.Test var identityOptions = new IdentityOptions(); identityOptions.SignIn.RequireConfirmedEmail = true; var logStore = new StringBuilder(); - var helper = SetupSignInManager(manager.Object, context, logStore, identityOptions); + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory, identityOptions); // Act var result = await helper.PasswordSignInAsync(user, "password", false, false); @@ -811,7 +861,7 @@ namespace Microsoft.AspNetCore.Identity.Test Assert.Equal(confirmed, result.Succeeded); Assert.NotEqual(confirmed, result.IsNotAllowed); - Assert.Equal(confirmed, !logStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed email.")); + Assert.Equal(confirmed, !loggerFactory.LogStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed email.")); manager.Verify(); auth.Verify(); @@ -846,8 +896,9 @@ namespace Microsoft.AspNetCore.Identity.Test var identityOptions = new IdentityOptions(); identityOptions.SignIn.RequireConfirmedPhoneNumber = true; - var logStore = new StringBuilder(); - var helper = SetupSignInManager(manager.Object, context, logStore, identityOptions); + + MockLoggerFactory loggerFactory = new MockLoggerFactory(); + var helper = SetupSignInManager(manager.Object, context, loggerFactory, identityOptions); // Act var result = await helper.PasswordSignInAsync(user, "password", false, false); @@ -855,7 +906,7 @@ namespace Microsoft.AspNetCore.Identity.Test // Assert Assert.Equal(confirmed, result.Succeeded); Assert.NotEqual(confirmed, result.IsNotAllowed); - Assert.Equal(confirmed, !logStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed phone number.")); + Assert.Equal(confirmed, !loggerFactory.LogStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed phone number.")); manager.Verify(); auth.Verify(); } diff --git a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/MockLoggerFactory.cs b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/MockLoggerFactory.cs new file mode 100644 index 0000000..b7ed4d9 --- /dev/null +++ b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/MockLoggerFactory.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.Identity.Test; +using Microsoft.Extensions.Logging; + +namespace AspNetCore.Identity.MongoDbCore.IntegrationTests +{ + public class MockLoggerFactory : ILoggerFactory + { + public StringBuilder LogStore; + + public List Loggers = new List(); + + public MockLoggerFactory() + { + LogStore = new StringBuilder(); + } + + public void AddProvider(ILoggerProvider provider) + { + + } + + public ILogger CreateLogger(string categoryName) + { + var logger = new MockLogger(categoryName, LogStore); + + Loggers.Add(logger); + + return logger; + } + + public ILogger CreateLogger() where T : class + { + //var logger = MockHelpers.MockILogger(LogStore).Object; + var logger = new MockLogger(LogStore); + + Loggers.Add(logger); + + return logger; + } + + public void Dispose() + { + } + } + + public class MockLogger : MockLogger + { + public String Name { get; protected set; } + public MockLogger(string name, StringBuilder store) : base(store) + { + Name = name; + } + } + + public class MockLogger : ILogger + { + public StringBuilder LogMessages; + + public MockLogger(StringBuilder store) + { + LogMessages = store; + } + + public IDisposable BeginScope(TState state) + { + return null; + } + + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (formatter != null) + { + LogMessages.Append(formatter(state, exception)); + } + else + { + LogMessages.Append(state.ToString()); + } + } + } + +} diff --git a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/Specification/UserManagerSpecificationTests.cs b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/Specification/UserManagerSpecificationTests.cs index 8143f26..47c12ab 100644 --- a/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/Specification/UserManagerSpecificationTests.cs +++ b/test/AspNetCore.Identity.MongoDbCore.IntegrationTests/Specification/UserManagerSpecificationTests.cs @@ -289,7 +289,7 @@ namespace Microsoft.AspNetCore.Identity.Test var manager = CreateManager(); var username = "Create" + Guid.NewGuid().ToString(); var user = CreateTestUser(username, useNamePrefixAsUserName: true); - var stamp = await manager.GetSecurityStampAsync(user); + Assert.Null(user.SecurityStamp); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); Assert.NotNull(await manager.GetSecurityStampAsync(user)); } @@ -947,10 +947,12 @@ namespace Microsoft.AspNetCore.Identity.Test } var manager = CreateManager(); var user = CreateTestUser(); - Assert.Null(await manager.GetSecurityStampAsync(user)); + var originalStamp = user.SecurityStamp; + //Update to library, can no longer test for null; throws an exceptoin + Assert.Null(user.SecurityStamp); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); var stamp = await manager.GetSecurityStampAsync(user); - Assert.NotNull(stamp); + Assert.NotEqual(originalStamp, stamp); IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); }