diff --git a/docs/8.0/ayanova/docs/home-tfa.md b/docs/8.0/ayanova/docs/home-tfa.md index 7a4175cb..17106eee 100644 --- a/docs/8.0/ayanova/docs/home-tfa.md +++ b/docs/8.0/ayanova/docs/home-tfa.md @@ -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 diff --git a/server/AyaNova/Controllers/AuthController.cs b/server/AyaNova/Controllers/AuthController.cs index 29faa18b..afbeed0b 100644 --- a/server/AyaNova/Controllers/AuthController.cs +++ b/server/AyaNova/Controllers/AuthController.cs @@ -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 TranslationKeysToFetch = new List { "AuthTwoFactor", "AuthEnterPin", "AuthVerifyCode", "Cancel","AuthPinInvalid" }; + List TranslationKeysToFetch = new List { "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 })); } + + + /// /// Generate TOTP secret and return for use in auth app /// @@ -639,13 +642,14 @@ namespace AyaNova.Api.Controllers /// - /// Disable (turn off) 2fa for current user account - /// - /// + /// Disable (turn off) 2fa for user account + /// (For other user id requires full privileges) + /// + /// Optional User id otherwise current user account /// From route path /// OK on success - [HttpPost("totp-disable")] - public async Task DisableTOTP(ApiVersion apiVersion) + [HttpPost("totp-disable/{id}")] + public async Task 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(); diff --git a/server/AyaNova/biz/UserBiz.cs b/server/AyaNova/biz/UserBiz.cs index 69ff5c8c..c114e637 100644 --- a/server/AyaNova/biz/UserBiz.cs +++ b/server/AyaNova/biz/UserBiz.cs @@ -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();