Skip to content

Commit 9344b15

Browse files
Return LockedOut from TwoFactorSignInAsync (#49337)
1 parent 1d6e871 commit 9344b15

2 files changed

Lines changed: 87 additions & 11 deletions

File tree

‎src/Identity/Core/src/SignInManager.cs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,11 @@ public virtual async Task<SignInResult> TwoFactorAuthenticatorSignInAsync(string
560560
// This is currently redundant, but it's here in case the code gets copied elsewhere.
561561
return SignInResult.Failed;
562562
}
563+
564+
if (await UserManager.IsLockedOutAsync(user))
565+
{
566+
return await LockedOut(user);
567+
}
563568
}
564569
return SignInResult.Failed;
565570
}
@@ -602,6 +607,11 @@ public virtual async Task<SignInResult> TwoFactorSignInAsync(string provider, st
602607
// This is currently redundant, but it's here in case the code gets copied elsewhere.
603608
return SignInResult.Failed;
604609
}
610+
611+
if (await UserManager.IsLockedOutAsync(user))
612+
{
613+
return await LockedOut(user);
614+
}
605615
}
606616
return SignInResult.Failed;
607617
}

‎src/Identity/test/Identity.Test/SignInManagerTest.cs‎

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,45 @@ public async Task TwoFactorAuthenticatorSignInFailWithoutLockout()
425425
auth.Verify();
426426
}
427427

428+
[Fact]
429+
public async Task TwoFactorAuthenticatorSignInAsyncReturnsLockedOut()
430+
{
431+
// Setup
432+
var user = new PocoUser { UserName = "Foo" };
433+
string providerName = "Authenticator";
434+
const string code = "3123";
435+
var manager = SetupUserManager(user);
436+
var lockedout = false;
437+
manager.Setup(m => m.AccessFailedAsync(user)).Returns(() =>
438+
{
439+
lockedout = true;
440+
return Task.FromResult(IdentityResult.Success);
441+
}).Verifiable();
442+
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
443+
manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, providerName ?? TokenOptions.DefaultAuthenticatorProvider, code)).ReturnsAsync(false).Verifiable();
444+
manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout));
445+
446+
var context = new DefaultHttpContext();
447+
var auth = MockAuth(context);
448+
var helper = SetupSignInManager(manager.Object, context);
449+
var twoFactorInfo = new SignInManager<PocoUser>.TwoFactorAuthenticationInfo { User = user };
450+
if (providerName != null)
451+
{
452+
helper.Options.Tokens.AuthenticatorTokenProvider = providerName;
453+
}
454+
var id = SignInManager<PocoUser>.StoreTwoFactorInfo(user.Id, null);
455+
auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorUserIdScheme))
456+
.ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, IdentityConstants.TwoFactorUserIdScheme))).Verifiable();
457+
458+
// Act
459+
var result = await helper.TwoFactorAuthenticatorSignInAsync(code, isPersistent: false, rememberClient: false);
460+
461+
// Assert
462+
Assert.True(result.IsLockedOut);
463+
manager.Verify();
464+
auth.Verify();
465+
}
466+
428467
[Theory]
429468
[InlineData(true, true, true)]
430469
[InlineData(true, true, false)]
@@ -670,6 +709,41 @@ public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout, bo
670709
auth.Verify();
671710
}
672711

712+
[Fact]
713+
public async Task TwoFactorSignInAsyncReturnsLockedOut()
714+
{
715+
// Setup
716+
var user = new PocoUser { UserName = "Foo" };
717+
var manager = SetupUserManager(user);
718+
var provider = "twofactorprovider";
719+
var code = "123456";
720+
var lockedout = false;
721+
manager.Setup(m => m.AccessFailedAsync(user)).Returns(() =>
722+
{
723+
lockedout = true;
724+
return Task.FromResult(IdentityResult.Success);
725+
}).Verifiable();
726+
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
727+
manager.Setup(m => m.IsLockedOutAsync(user)).Returns(() => Task.FromResult(lockedout));
728+
manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code)).ReturnsAsync(false).Verifiable();
729+
730+
var context = new DefaultHttpContext();
731+
var auth = MockAuth(context);
732+
var helper = SetupSignInManager(manager.Object, context);
733+
var id = SignInManager<PocoUser>.StoreTwoFactorInfo(user.Id, loginProvider: null);
734+
735+
auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.TwoFactorUserIdScheme))
736+
.ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, IdentityConstants.TwoFactorUserIdScheme))).Verifiable();
737+
738+
// Act
739+
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent: false, rememberClient: false);
740+
741+
// Assert
742+
Assert.True(result.IsLockedOut);
743+
manager.Verify();
744+
auth.Verify();
745+
}
746+
673747
[Fact]
674748
public async Task RememberClientStoresUserId()
675749
{
@@ -1169,16 +1243,9 @@ public async Task CheckPasswordSignInLockedOutResultIsDependentOnTheAccessFailed
11691243
manager.Verify();
11701244
}
11711245

1172-
public static object[][] AccessFailedResults => new object[][]
1173-
{
1174-
new object[] { IdentityResult.Success },
1175-
new object[] { null },
1176-
new object[] { IdentityResult.Failed() },
1177-
};
1178-
11791246
[Theory]
1180-
[MemberData(nameof(AccessFailedResults))]
1181-
public async Task TwoFactorSignInLockedOutResultIsAlwaysGenericFailureRegardlessOfTheAccessFailedAsyncResult(IdentityResult accessFailedResult)
1247+
[MemberData(nameof(ExpectedLockedOutSignInResultsGivenAccessFailedResults))]
1248+
public async Task TwoFactorSignInLockedOutResultIsDependentOnTheAccessFailedAsyncResult(IdentityResult accessFailedResult, SignInResult expectedSignInResult)
11821249
{
11831250
// Setup
11841251
var isLockedOutCallCount = 0;
@@ -1204,8 +1271,7 @@ public async Task TwoFactorSignInLockedOutResultIsAlwaysGenericFailureRegardless
12041271
var result = await helper.TwoFactorSignInAsync(provider, code, false, false);
12051272

12061273
// Assert
1207-
// Unlike password sign in, 2fa always returns SignInResult.Failed rather than LockedOut.
1208-
Assert.Same(SignInResult.Failed, result);
1274+
Assert.Same(expectedSignInResult, result);
12091275
manager.Verify();
12101276
auth.Verify();
12111277
}

0 commit comments

Comments
 (0)