🐛 Fixes and overhaul the auth experience
This commit is contained in:
		| @@ -141,6 +141,8 @@ public class AccountAuthFactor : ModelBase | ||||
|             case AccountAuthFactorType.TimedCode: | ||||
|                 var otp = new Totp(Base32Encoding.ToBytes(Secret)); | ||||
|                 return otp.VerifyTotp(DateTime.UtcNow, password, out _, new VerificationWindow(previous: 5, future: 5)); | ||||
|             case AccountAuthFactorType.EmailCode: | ||||
|             case AccountAuthFactorType.InAppCode: | ||||
|             default: | ||||
|                 throw new InvalidOperationException("Unsupported verification type, use CheckDeliveredCode instead."); | ||||
|         } | ||||
|   | ||||
| @@ -357,7 +357,7 @@ public class AccountCurrentController( | ||||
|  | ||||
|     [HttpPost("factors/{id:guid}/enable")] | ||||
|     [Authorize] | ||||
|     public async Task<ActionResult<AccountAuthFactor>> EnableAuthFactor(Guid id, [FromBody] string code) | ||||
|     public async Task<ActionResult<AccountAuthFactor>> EnableAuthFactor(Guid id, [FromBody] string? code) | ||||
|     { | ||||
|         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||
|  | ||||
|   | ||||
| @@ -157,13 +157,16 @@ public class AccountService( | ||||
|         return factor; | ||||
|     } | ||||
|  | ||||
|     public async Task<AccountAuthFactor> EnableAuthFactor(AccountAuthFactor factor, string code) | ||||
|     public async Task<AccountAuthFactor> EnableAuthFactor(AccountAuthFactor factor, string? code) | ||||
|     { | ||||
|         if (factor.EnabledAt is not null) throw new ArgumentException("The factor has been enabled."); | ||||
|         if (!factor.VerifyPassword(code)) | ||||
|             throw new InvalidOperationException( | ||||
|                 "Invalid code, you need to enter the correct code to enable the factor." | ||||
|             ); | ||||
|         if (factor.Type is AccountAuthFactorType.Password or AccountAuthFactorType.TimedCode) | ||||
|         { | ||||
|             if (code is null || !factor.VerifyPassword(code)) | ||||
|                 throw new InvalidOperationException( | ||||
|                     "Invalid code, you need to enter the correct code to enable the factor." | ||||
|                 ); | ||||
|         } | ||||
|  | ||||
|         factor.EnabledAt = SystemClock.Instance.GetCurrentInstant(); | ||||
|         db.Update(factor); | ||||
| @@ -186,7 +189,7 @@ public class AccountService( | ||||
|         factor.EnabledAt = null; | ||||
|         db.Update(factor); | ||||
|         await db.SaveChangesAsync(); | ||||
|          | ||||
|  | ||||
|         return factor; | ||||
|     } | ||||
|  | ||||
| @@ -219,7 +222,7 @@ public class AccountService( | ||||
|             case AccountAuthFactorType.InAppCode: | ||||
|                 if (await _GetFactorCode(factor) is not null) | ||||
|                     throw new InvalidOperationException("A factor code has been sent and in active duration."); | ||||
|                  | ||||
|  | ||||
|                 await nty.SendNotification( | ||||
|                     account, | ||||
|                     "auth.verification", | ||||
| @@ -233,7 +236,7 @@ public class AccountService( | ||||
|             case AccountAuthFactorType.EmailCode: | ||||
|                 if (await _GetFactorCode(factor) is not null) | ||||
|                     throw new InvalidOperationException("A factor code has been sent and in active duration."); | ||||
|                  | ||||
|  | ||||
|                 ArgumentNullException.ThrowIfNull(hint); | ||||
|                 hint = hint.Replace("@", "").Replace(".", "").Replace("+", "").Replace("%", ""); | ||||
|                 if (string.IsNullOrWhiteSpace(hint)) | ||||
| @@ -263,8 +266,8 @@ public class AccountService( | ||||
|                 } | ||||
|  | ||||
|                 await email.SendTemplatedEmailAsync<VerificationEmail, VerificationEmailModel>( | ||||
|                     account.Nick, | ||||
|                     contact.Content, | ||||
|                     localizer["EmailVerificationTitle"], | ||||
|                     localizer["VerificationEmail"], | ||||
|                     new VerificationEmailModel | ||||
|                     { | ||||
|   | ||||
| @@ -53,7 +53,7 @@ public class AuthController( | ||||
|         var challenge = new Challenge | ||||
|         { | ||||
|             ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)), | ||||
|             StepTotal = 1, | ||||
|             StepTotal = 3, | ||||
|             Platform = request.Platform, | ||||
|             Audiences = request.Audiences, | ||||
|             Scopes = request.Scopes, | ||||
| @@ -80,10 +80,11 @@ public class AuthController( | ||||
|         var challenge = await db.AuthChallenges | ||||
|             .Include(e => e.Account) | ||||
|             .Include(e => e.Account.AuthFactors) | ||||
|             .Where(e => e.Id == id).FirstOrDefaultAsync(); | ||||
|             .Where(e => e.Id == id) | ||||
|             .FirstOrDefaultAsync(); | ||||
|         return challenge is null | ||||
|             ? NotFound("Auth challenge was not found.") | ||||
|             : challenge.Account.AuthFactors.ToList(); | ||||
|             : challenge.Account.AuthFactors.Where(e => e.EnabledAt != null).ToList(); | ||||
|     } | ||||
|  | ||||
|     [HttpPost("challenge/{id:guid}/factors/{factorId:guid}")] | ||||
| @@ -131,6 +132,8 @@ public class AuthController( | ||||
|  | ||||
|         var factor = await db.AccountAuthFactors.FindAsync(request.FactorId); | ||||
|         if (factor is null) return NotFound("Auth factor was not found."); | ||||
|         if (factor.EnabledAt is null) return BadRequest("Auth factor is not enabled."); | ||||
|         if (factor.Trustworthy <= 0) return BadRequest("Auth factor is not trustworthy."); | ||||
|  | ||||
|         if (challenge.StepRemain == 0) return challenge; | ||||
|         if (challenge.ExpiredAt.HasValue && challenge.ExpiredAt.Value < Instant.FromDateTimeUtc(DateTime.UtcNow)) | ||||
| @@ -140,7 +143,8 @@ public class AuthController( | ||||
|         { | ||||
|             if (await accounts.VerifyFactorCode(factor, request.Password)) | ||||
|             { | ||||
|                 challenge.StepRemain--; | ||||
|                 challenge.StepRemain -= factor.Trustworthy; | ||||
|                 challenge.StepRemain = Math.Max(0, challenge.StepRemain); | ||||
|                 challenge.BlacklistFactors.Add(factor.Id); | ||||
|                 db.Update(challenge); | ||||
|                 als.CreateActionLogFromRequest(ActionLogType.ChallengeSuccess, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user