Skip to content
Open
6 changes: 6 additions & 0 deletions src/Identity/Extensions.Core/src/LockoutOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@ public class LockoutOptions
/// </summary>
/// <value>The <see cref="TimeSpan"/> a user is locked out for when a lockout occurs.</value>
public TimeSpan DefaultLockoutTimeSpan { get; set; } = TimeSpan.FromMinutes(5);

/// <summary>
/// Specifies whether the lockout should be permanent.
/// If true, the user is locked out.
/// </summary>
public bool PermanentLockout { get; set; }
}
2 changes: 2 additions & 0 deletions src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#nullable enable
*REMOVED*Microsoft.AspNetCore.Identity.UserLoginInfo.UserLoginInfo(string! loginProvider, string! providerKey, string? displayName) -> void
Microsoft.AspNetCore.Identity.UserLoginInfo.UserLoginInfo(string! loginProvider, string! providerKey, string? providerDisplayName) -> void
Microsoft.AspNetCore.Identity.LockoutOptions.PermanentLockout.get -> bool
Microsoft.AspNetCore.Identity.LockoutOptions.PermanentLockout.set -> void
21 changes: 18 additions & 3 deletions src/Identity/Extensions.Core/src/UserManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1818,15 +1818,30 @@ public virtual async Task<IdentityResult> AccessFailedAsync(TUser user)
var store = GetUserLockoutStore();
ArgumentNullThrowHelper.ThrowIfNull(user);

// If this puts the user over the threshold for lockout, lock them out and reset the access failed count
// If PermanentLockout is enabled, lock the user indefinitely
if (Options.Lockout.PermanentLockout)
{
Logger.LogDebug(LoggerEventIds.UserLockedOut, "User is permanently locked out.");
await store.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue, CancellationToken).ConfigureAwait(false);
return await UpdateUserAsync(user).ConfigureAwait(false);
}

// Increment access failed count
var count = await store.IncrementAccessFailedCountAsync(user, CancellationToken).ConfigureAwait(false);
if (count < Options.Lockout.MaxFailedAccessAttempts)
{
return await UpdateUserAsync(user).ConfigureAwait(false);
}

Logger.LogDebug(LoggerEventIds.UserLockedOut, "User is locked out.");
await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan),
CancellationToken).ConfigureAwait(false);

// Set the lockout time based on configuration.
var now = DateTimeOffset.UtcNow;
DateTimeOffset lockoutEnd = Options.Lockout.DefaultLockoutTimeSpan == TimeSpan.MaxValue
? DateTimeOffset.MaxValue
: now.Add(Options.Lockout.DefaultLockoutTimeSpan);

await store.SetLockoutEndDateAsync(user, lockoutEnd, CancellationToken).ConfigureAwait(false);
await store.ResetAccessFailedCountAsync(user, CancellationToken).ConfigureAwait(false);
return await UpdateUserAsync(user).ConfigureAwait(false);
}
Expand Down
36 changes: 36 additions & 0 deletions src/Identity/test/Identity.Test/UserManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,42 @@
IdentityResultAssert.IsSuccess(await manager.ResetAccessFailedCountAsync(user));
}

[Fact]
public async Task AccessFailedAsyncIncrementsAccessFailedCount()
{
// Arrange
var user = new PocoUser() { UserName = "testuser" };
var store = new Mock<IUserLockoutStore<PocoUser>>();
int failedCount = 1;

store.Setup(x => x.SupportsUserLockout).Returns(true);

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 1023 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Identity/test/Identity.Test/UserManagerTest.cs#L1023

src/Identity/test/Identity.Test/UserManagerTest.cs(1023,28): error CS1061: (NETCORE_ENGINEERING_TELEMETRY=Build) 'IUserLockoutStore<PocoUser>' does not contain a definition for 'SupportsUserLockout' and no accessible extension method 'SupportsUserLockout' accepting a first argument of type 'IUserLockoutStore<PocoUser>' could be found (are you missing a using directive or an assembly reference?)

store.Setup(x => x.GetAccessFailedCountAsync(user, It.IsAny<CancellationToken>()))
.ReturnsAsync(() => failedCount);

store.Setup(x => x.IncrementAccessFailedCountAsync(user, It.IsAny<CancellationToken>()))
.Callback(() => failedCount++)
.ReturnsAsync(() => failedCount);

store.Setup(x => x.UpdateAsync(user, It.IsAny<CancellationToken>()))
.ReturnsAsync(IdentityResult.Success);

var manager = MockHelpers.TestUserManager(store.Object);
manager?.Options?.Lockout?.PermanentLockout = false;

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

Check failure on line 1036 in src/Identity/test/Identity.Test/UserManagerTest.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Identity/test/Identity.Test/UserManagerTest.cs#L1036

src/Identity/test/Identity.Test/UserManagerTest.cs(1036,9): error CS0131: (NETCORE_ENGINEERING_TELEMETRY=Build) The left-hand side of an assignment must be a variable, property or indexer

// Act
var result = await manager.AccessFailedAsync(user);

// Assert
Assert.NotNull(result);
Assert.True(result.Succeeded, "AccessFailedAsync should return success.");

store.Verify(x => x.IncrementAccessFailedCountAsync(user, It.IsAny<CancellationToken>()), Times.Once);

var newFailedCount = await manager.GetAccessFailedCountAsync(user);
Assert.Equal(2, newFailedCount);
}

[Fact]
public async Task ManagerPublicNullChecks()
{
Expand Down
Loading