🐛 Fixes and overhaul the auth experience
This commit is contained in:
parent
2f051d0615
commit
0e78f7f7d2
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user