This commit is contained in:
2021-03-12 21:28:07 +00:00
parent 6b307280d9
commit 61c64dbcf4
3 changed files with 37 additions and 23 deletions

View File

@@ -5,22 +5,24 @@
AyaNova supports Two-Factor authentication ("TFA") as an additional security measure for any User account. The first "factor" in TFA is the user name and password as normal, the second "factor" is a 6 digit passcode that changes every 30 seconds and is unique for every AyaNova User. Passcodes are generated in an App running on a device that you "link" to your AyaNova account via the. Enabling TFA means that even if a login name and password were to be accidentally exposed a malicious user would still not be able to login unless they had that User's device with their TFA authentication app available. We strongly recommend all users enable TFA, including and specifically the SuperUser account.
## Enabling / Disabling TFA
## Enabling TFA
Two-Factor Authentication is enabled from the "Two Factor Authentication" menu option in the form `Home -> User Settings` menu.
Due to the nature of TFA it is not possible for an Administrator to set this up on behalf of a User, it must be done logged in *as* the User account with their device containing their TFA authentication App in hand.
## Disabling TFA
Two-Factor Authentication is disabled by the user from the "Two Factor Authentication" menu option in the form `Home -> User Settings` menu.
A User with rights to edit other User accounts can disable TFA for any User from the `Adminstration -> User` edit form's menu.
Two-Factor Authentication is enabled (and disabled) from the "Two Factor Authentication" menu option in the form `Home -> User Settings` menu.
Due to the nature of TFA it is not possible for an administrator to set this up on behalf of a User, it must be done logged in *as* the User account with their device containing their TFA authentication App in hand.
## TFA Apps
There are many Two-Factor Authentication apps available for all device types. Here are some that have been tested with AyaNova specfically:
There are many Two-Factor Authentication apps freely available for all device types. Here are some that have been tested with AyaNova specfically:
* [DUO](https://duo.com/product/multi-factor-authentication-mfa/duo-mobile-app)
* [.NET Core Test Explorer](https://github.com/formulahendry/vscode-dotnet-test-explorer)
* [Google Authenticator for IOS](https://apps.apple.com/us/app/google-authenticator/id388497605)
* [Google Authenticator for Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2)
* [Microsoft Authenticator (IOS / Android)](https://www.microsoft.com/en-us/account/authenticator)
* [Authy](https://authy.com/)
Authentication apps:
DUO
https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2
https://play.google.com/store/apps/details?id=com.azure.authenticator
https://play.google.com/store/apps/details?id=com.authy.authy

View File

@@ -168,16 +168,16 @@ namespace AyaNova.Api.Controllers
string hashed = Hasher.hash(u.Salt, creds.Password);
if (hashed == u.Password)
{
//TWO FACTOR ENABLED??
//TWO FACTOR ENABLED??
//if 2fa enabled then need to validate it before sending token, so we're halfway there and need to send a 2fa prompt
if (u.TwoFactorEnabled)
{
//Generate a temporary token to identify and verify this is the same user
u.TempToken = Hasher.GenerateSalt().Replace("=", "").Replace("+", "");
await ct.SaveChangesAsync();
var UOpt=await ct.UserOptions.AsNoTracking().FirstAsync(z=>z.UserId==u.Id);
var UOpt = await ct.UserOptions.AsNoTracking().FirstAsync(z => z.UserId == u.Id);
List<string> TranslationKeysToFetch = new List<string> { "AuthTwoFactor", "AuthEnterPin", "AuthVerifyCode", "Cancel","AuthPinInvalid" };
List<string> TranslationKeysToFetch = new List<string> { "AuthTwoFactor", "AuthEnterPin", "AuthVerifyCode", "Cancel", "AuthPinInvalid" };
var LT = await TranslationBiz.GetSubsetStaticAsync(TranslationKeysToFetch, UOpt.TranslationId);
return Ok(ApiOkResponse.Response(new
@@ -185,7 +185,7 @@ namespace AyaNova.Api.Controllers
AuthTwoFactor = LT["AuthTwoFactor"],
AuthEnterPin = LT["AuthEnterPin"],
AuthVerifyCode = LT["AuthVerifyCode"],
AuthPinInvalid=LT["AuthPinInvalid"],
AuthPinInvalid = LT["AuthPinInvalid"],
Cancel = LT["Cancel"],
tfa = true,
tt = u.TempToken
@@ -195,7 +195,7 @@ namespace AyaNova.Api.Controllers
//Not 2fa, Valid password, user is authorized
return await ReturnUserCredsOnSuccessfulAuthentication(u);
}
}
@@ -530,6 +530,9 @@ namespace AyaNova.Api.Controllers
}));
}
/// <summary>
/// Generate TOTP secret and return for use in auth app
///
@@ -639,13 +642,14 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Disable (turn off) 2fa for current user account
///
/// </summary>
/// Disable (turn off) 2fa for user account
/// (For other user id requires full privileges)
/// </summary>
/// <param name="id">Optional User id otherwise current user account</param>
/// <param name="apiVersion">From route path</param>
/// <returns>OK on success</returns>
[HttpPost("totp-disable")]
public async Task<IActionResult> DisableTOTP(ApiVersion apiVersion)
[HttpPost("totp-disable/{id}")]
public async Task<IActionResult> DisableTOTP([FromRoute] long? id, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
@@ -653,14 +657,21 @@ namespace AyaNova.Api.Controllers
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
if (id != null)
{
if (!Authorized.HasModifyRole(HttpContext.Items, AyaType.User))
return StatusCode(403, new ApiNotAuthorizedResponse());
}
//get user
var UserId = UserIdFromContext.Id(HttpContext.Items);
var UserId = id ?? UserIdFromContext.Id(HttpContext.Items);
var u = await ct.User.FirstOrDefaultAsync(z => z.Id == UserId);
if (u == null)//should never happen but ?
return StatusCode(403, new ApiNotAuthorizedResponse());
u.TotpSecret = null;
u.TempToken = null;
u.TwoFactorEnabled = false;
await ct.SaveChangesAsync();
return NoContent();

View File

@@ -495,6 +495,7 @@ namespace AyaNova.Biz
}
var ResetCode = Hasher.GetRandomAlphanumericString(32);
dbObject.PasswordResetCode = ResetCode;
dbObject.PasswordResetCodeExpire = DateTime.UtcNow.AddHours(48);//This is not enough time to issue a reset code on a friday at 5pm and use it Monday before noon, but it is more understandable and clear
await ct.SaveChangesAsync();