diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs index ddedc15c3b..87cf6d2579 100644 --- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs @@ -1247,6 +1247,10 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore { throw new ArgumentNullException(nameof(user)); } + if (stamp == null) + { + throw new ArgumentNullException(nameof(stamp)); + } user.SecurityStamp = stamp; return TaskCache.CompletedTask; } diff --git a/src/Microsoft.AspNetCore.Identity/IdentityErrorDescriber.cs b/src/Microsoft.AspNetCore.Identity/IdentityErrorDescriber.cs index 54c024c79e..914e82e5ea 100644 --- a/src/Microsoft.AspNetCore.Identity/IdentityErrorDescriber.cs +++ b/src/Microsoft.AspNetCore.Identity/IdentityErrorDescriber.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Identity /// /// Returns the default . /// - /// The default , + /// The default . public virtual IdentityError DefaultError() { return new IdentityError diff --git a/src/Microsoft.AspNetCore.Identity/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Identity/Properties/Resources.Designer.cs index 0029258790..fa00dea7f6 100644 --- a/src/Microsoft.AspNetCore.Identity/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Identity/Properties/Resources.Designer.cs @@ -250,6 +250,22 @@ namespace Microsoft.AspNetCore.Identity return string.Format(CultureInfo.CurrentCulture, GetString("NoTokenProvider"), p0); } + /// + /// User security stamp cannot be null. + /// + internal static string NullSecurityStamp + { + get { return GetString("NullSecurityStamp"); } + } + + /// + /// User security stamp cannot be null. + /// + internal static string FormatNullSecurityStamp() + { + return GetString("NullSecurityStamp"); + } + /// /// Incorrect password. /// diff --git a/src/Microsoft.AspNetCore.Identity/Resources.resx b/src/Microsoft.AspNetCore.Identity/Resources.resx index 85e16fc30d..3e0512b167 100644 --- a/src/Microsoft.AspNetCore.Identity/Resources.resx +++ b/src/Microsoft.AspNetCore.Identity/Resources.resx @@ -177,6 +177,10 @@ No IUserTokenProvider named '{0}' is registered. Error when there is no IUserTokenProvider + + User security stamp cannot be null. + Error when a user's security stamp is null. + Incorrect password. Error when a password doesn't match diff --git a/src/Microsoft.AspNetCore.Identity/UserManager.cs b/src/Microsoft.AspNetCore.Identity/UserManager.cs index d3eff1b91d..ffd67bd867 100644 --- a/src/Microsoft.AspNetCore.Identity/UserManager.cs +++ b/src/Microsoft.AspNetCore.Identity/UserManager.cs @@ -2236,6 +2236,14 @@ namespace Microsoft.AspNetCore.Identity private async Task ValidateUserInternal(TUser user) { + if (SupportsUserSecurityStamp) + { + var stamp = await GetSecurityStampAsync(user); + if (stamp == null) + { + throw new InvalidOperationException(Resources.NullSecurityStamp); + } + } var errors = new List(); foreach (var v in UserValidators) { diff --git a/test/Microsoft.AspNetCore.Identity.Test/UserManagerTest.cs b/test/Microsoft.AspNetCore.Identity.Test/UserManagerTest.cs index 664bc14f9f..61a0e1e0ec 100644 --- a/test/Microsoft.AspNetCore.Identity.Test/UserManagerTest.cs +++ b/test/Microsoft.AspNetCore.Identity.Test/UserManagerTest.cs @@ -483,6 +483,42 @@ namespace Microsoft.AspNetCore.Identity.Test 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(null).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(null).Verifiable(); + + // Act + // Assert + var ex = await Assert.ThrowsAsync(() => manager.UpdateAsync(user)); + Assert.Contains(Resources.NullSecurityStamp, ex.Message); + + store.VerifyAll(); + } + + + [Fact] public async Task RemoveClaimsCallsStore() {