Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Tests/Resgrid.Tests/Security/WebLoginMfaTransactionTests.Shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,32 @@ public async Task The_older_second_factor_page_never_remembers_a_shared_workstat
_signIn.Verify(s => s.TwoFactorAuthenticatorSignInAsync("123456", It.IsAny<bool>(), true), Times.Once);
}

[Test]
public async Task The_older_second_factor_page_restarts_sign_in_when_the_partial_sign_in_expired()
{
_signIn.Setup(s => s.GetTwoFactorAuthenticationUserAsync()).ReturnsAsync((IdentityUser)null);

var expired = Browser();
var result = await expired.Controller.LoginWith2fa(new VerifyCodeViewModel { Code = "123456" }, CancellationToken.None);

result.Should().BeOfType<RedirectToActionResult>().Which.ActionName.Should().Be("LogOn");
expired.Controller.TempData["LoginMfaMessage"].Should().Be("LoginMfaExpired");
_signIn.Verify(s => s.TwoFactorAuthenticatorSignInAsync(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>()), Times.Never);
}

[Test]
public async Task The_older_recovery_code_page_restarts_sign_in_when_the_partial_sign_in_expired()
{
_signIn.Setup(s => s.GetTwoFactorAuthenticationUserAsync()).ReturnsAsync((IdentityUser)null);

var expired = Browser();
var result = await expired.Controller.LoginWithRecoveryCode(new VerifyCodeViewModel { Code = "ABCD-1234" }, CancellationToken.None);

result.Should().BeOfType<RedirectToActionResult>().Which.ActionName.Should().Be("LogOn");
expired.Controller.TempData["LoginMfaMessage"].Should().Be("LoginMfaExpired");
_signIn.Verify(s => s.TwoFactorRecoveryCodeSignInAsync(It.IsAny<string>()), Times.Never);
}

[Test]
public async Task The_sign_in_page_names_the_workstation_only_where_shared_mode_is_on_and_says_when_a_shift_ended()
{
Expand Down
13 changes: 9 additions & 4 deletions Web/Resgrid.Web/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,11 @@ public async Task<IActionResult> LoginWith2fa(VerifyCodeViewModel model, Cancell
{
if (!ModelState.IsValid) return View(model);

// Fetch the user before sign-in while the partial 2FA cookie is still present
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync()
?? await _userManager.FindByNameAsync(model.Provider ?? string.Empty);
// Fetch the user before sign-in while the partial 2FA cookie is still present. Without it (the partial sign-in expired
// or was never made) there is nothing to verify the code against, so the user starts again from the password screen.
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
return RestartSignIn(MfaLoginTransactionOutcome.Expired, returnUrl ?? model.ReturnUrl);

var code = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(code, model.RememberMe,
Expand Down Expand Up @@ -586,8 +588,11 @@ public async Task<IActionResult> LoginWithRecoveryCode(VerifyCodeViewModel model

if (!ModelState.IsValid) return View(model);

// Fetch the user before sign-in while the partial 2FA cookie is still present
// Fetch the user before sign-in while the partial 2FA cookie is still present. Without it (the partial sign-in expired
// or was never made) there is nothing to verify the code against, so the user starts again from the password screen.
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
return RestartSignIn(MfaLoginTransactionOutcome.Expired, returnUrl ?? model.ReturnUrl);

var recoveryCode = model.Code.Replace(" ", string.Empty);
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
Expand Down
Loading