Skip to content

Commit

Permalink
Remove 2factor support in SampleIdSrv4
Browse files Browse the repository at this point in the history
- The 2FA support is not important and the template contained vulnerable code that could
  pose a risk to anyone copying the sample code as-is.
  • Loading branch information
AndersAbel committed Jan 5, 2021
1 parent abe522b commit 5cc607f
Show file tree
Hide file tree
Showing 11 changed files with 1 addition and 540 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public async Task<IActionResult> Login(LoginViewModel model, string returnUrl =
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
throw new NotSupportedException();
}
if (result.IsLockedOut)
{
Expand All @@ -107,116 +107,6 @@ public async Task<IActionResult> Login(LoginViewModel model, string returnUrl =
return View(model);
}

[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null)
{
// Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();

if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}

var model = new LoginWith2faViewModel { RememberMe = rememberMe };
ViewData["ReturnUrl"] = returnUrl;

return View(model);
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWith2fa(LoginWith2faViewModel model, bool rememberMe, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return View(model);
}

var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);

var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, model.RememberMachine);

if (result.Succeeded)
{
_logger.LogInformation("User with ID {UserId} logged in with 2fa.", user.Id);
return RedirectToLocal(returnUrl);
}
else if (result.IsLockedOut)
{
_logger.LogWarning("User with ID {UserId} account locked out.", user.Id);
return RedirectToAction(nameof(Lockout));
}
else
{
_logger.LogWarning("Invalid authenticator code entered for user with ID {UserId}.", user.Id);
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
return View();
}
}

[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> LoginWithRecoveryCode(string returnUrl = null)
{
// Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}

ViewData["ReturnUrl"] = returnUrl;

return View();
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return View(model);
}

var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}

var recoveryCode = model.RecoveryCode.Replace(" ", string.Empty);

var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);

if (result.Succeeded)
{
_logger.LogInformation("User with ID {UserId} logged in with a recovery code.", user.Id);
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
_logger.LogWarning("User with ID {UserId} account locked out.", user.Id);
return RedirectToAction(nameof(Lockout));
}
else
{
_logger.LogWarning("Invalid recovery code entered for user with ID {UserId}", user.Id);
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
return View();
}
}

[HttpGet]
[AllowAnonymous]
public IActionResult Lockout()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ public class ManageController : Controller
private readonly ILogger _logger;
private readonly UrlEncoder _urlEncoder;

private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";

public ManageController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
Expand Down Expand Up @@ -306,164 +304,6 @@ public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel model)
return RedirectToAction(nameof(ExternalLogins));
}

[HttpGet]
public async Task<IActionResult> TwoFactorAuthentication()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

var model = new TwoFactorAuthenticationViewModel
{
HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null,
Is2faEnabled = user.TwoFactorEnabled,
RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user),
};

return View(model);
}

[HttpGet]
public async Task<IActionResult> Disable2faWarning()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

if (!user.TwoFactorEnabled)
{
throw new ApplicationException($"Unexpected error occured disabling 2FA for user with ID '{user.Id}'.");
}

return View(nameof(Disable2fa));
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Disable2fa()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false);
if (!disable2faResult.Succeeded)
{
throw new ApplicationException($"Unexpected error occured disabling 2FA for user with ID '{user.Id}'.");
}

_logger.LogInformation("User with ID {UserId} has disabled 2fa.", user.Id);
return RedirectToAction(nameof(TwoFactorAuthentication));
}

[HttpGet]
public async Task<IActionResult> EnableAuthenticator()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
if (string.IsNullOrEmpty(unformattedKey))
{
await _userManager.ResetAuthenticatorKeyAsync(user);
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
}

var model = new EnableAuthenticatorViewModel
{
SharedKey = FormatKey(unformattedKey),
AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey)
};

return View(model);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EnableAuthenticator(EnableAuthenticatorViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}

var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

// Strip spaces and hypens
var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);

var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);

if (!is2faTokenValid)
{
ModelState.AddModelError("model.Code", "Verification code is invalid.");
return View(model);
}

await _userManager.SetTwoFactorEnabledAsync(user, true);
_logger.LogInformation("User with ID {UserId} has enabled 2FA with an authenticator app.", user.Id);
return RedirectToAction(nameof(GenerateRecoveryCodes));
}

[HttpGet]
public IActionResult ResetAuthenticatorWarning()
{
return View(nameof(ResetAuthenticator));
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetAuthenticator()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

await _userManager.SetTwoFactorEnabledAsync(user, false);
await _userManager.ResetAuthenticatorKeyAsync(user);
_logger.LogInformation("User with id '{UserId}' has reset their authentication app key.", user.Id);

return RedirectToAction(nameof(EnableAuthenticator));
}

[HttpGet]
public async Task<IActionResult> GenerateRecoveryCodes()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

if (!user.TwoFactorEnabled)
{
throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled.");
}

var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
var model = new GenerateRecoveryCodesViewModel { RecoveryCodes = recoveryCodes.ToArray() };

_logger.LogInformation("User with ID {UserId} has generated new 2FA recovery codes.", user.Id);

return View(model);
}

#region Helpers

private void AddErrors(IdentityResult result)
Expand All @@ -474,32 +314,6 @@ private void AddErrors(IdentityResult result)
}
}

private string FormatKey(string unformattedKey)
{
var result = new StringBuilder();
int currentPosition = 0;
while (currentPosition + 4 < unformattedKey.Length)
{
result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
currentPosition += 4;
}
if (currentPosition < unformattedKey.Length)
{
result.Append(unformattedKey.Substring(currentPosition));
}

return result.ToString().ToLowerInvariant();
}

private string GenerateQrCodeUri(string email, string unformattedKey)
{
return string.Format(
AuthenicatorUriFormat,
_urlEncoder.Encode("SampleIdentityServer4AspNetIdentity"),
_urlEncoder.Encode(email),
unformattedKey);
}

#endregion
}
}

This file was deleted.

0 comments on commit 5cc607f

Please sign in to comment.