🐛 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:
|
case AccountAuthFactorType.TimedCode:
|
||||||
var otp = new Totp(Base32Encoding.ToBytes(Secret));
|
var otp = new Totp(Base32Encoding.ToBytes(Secret));
|
||||||
return otp.VerifyTotp(DateTime.UtcNow, password, out _, new VerificationWindow(previous: 5, future: 5));
|
return otp.VerifyTotp(DateTime.UtcNow, password, out _, new VerificationWindow(previous: 5, future: 5));
|
||||||
|
case AccountAuthFactorType.EmailCode:
|
||||||
|
case AccountAuthFactorType.InAppCode:
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException("Unsupported verification type, use CheckDeliveredCode instead.");
|
throw new InvalidOperationException("Unsupported verification type, use CheckDeliveredCode instead.");
|
||||||
}
|
}
|
||||||
|
@ -357,7 +357,7 @@ public class AccountCurrentController(
|
|||||||
|
|
||||||
[HttpPost("factors/{id:guid}/enable")]
|
[HttpPost("factors/{id:guid}/enable")]
|
||||||
[Authorize]
|
[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();
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
@ -157,13 +157,16 @@ public class AccountService(
|
|||||||
return factor;
|
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.EnabledAt is not null) throw new ArgumentException("The factor has been enabled.");
|
||||||
if (!factor.VerifyPassword(code))
|
if (factor.Type is AccountAuthFactorType.Password or AccountAuthFactorType.TimedCode)
|
||||||
throw new InvalidOperationException(
|
{
|
||||||
"Invalid code, you need to enter the correct code to enable the factor."
|
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();
|
factor.EnabledAt = SystemClock.Instance.GetCurrentInstant();
|
||||||
db.Update(factor);
|
db.Update(factor);
|
||||||
@ -263,8 +266,8 @@ public class AccountService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
await email.SendTemplatedEmailAsync<VerificationEmail, VerificationEmailModel>(
|
await email.SendTemplatedEmailAsync<VerificationEmail, VerificationEmailModel>(
|
||||||
|
account.Nick,
|
||||||
contact.Content,
|
contact.Content,
|
||||||
localizer["EmailVerificationTitle"],
|
|
||||||
localizer["VerificationEmail"],
|
localizer["VerificationEmail"],
|
||||||
new VerificationEmailModel
|
new VerificationEmailModel
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ public class AuthController(
|
|||||||
var challenge = new Challenge
|
var challenge = new Challenge
|
||||||
{
|
{
|
||||||
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)),
|
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)),
|
||||||
StepTotal = 1,
|
StepTotal = 3,
|
||||||
Platform = request.Platform,
|
Platform = request.Platform,
|
||||||
Audiences = request.Audiences,
|
Audiences = request.Audiences,
|
||||||
Scopes = request.Scopes,
|
Scopes = request.Scopes,
|
||||||
@ -80,10 +80,11 @@ public class AuthController(
|
|||||||
var challenge = await db.AuthChallenges
|
var challenge = await db.AuthChallenges
|
||||||
.Include(e => e.Account)
|
.Include(e => e.Account)
|
||||||
.Include(e => e.Account.AuthFactors)
|
.Include(e => e.Account.AuthFactors)
|
||||||
.Where(e => e.Id == id).FirstOrDefaultAsync();
|
.Where(e => e.Id == id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
return challenge is null
|
return challenge is null
|
||||||
? NotFound("Auth challenge was not found.")
|
? 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}")]
|
[HttpPost("challenge/{id:guid}/factors/{factorId:guid}")]
|
||||||
@ -131,6 +132,8 @@ public class AuthController(
|
|||||||
|
|
||||||
var factor = await db.AccountAuthFactors.FindAsync(request.FactorId);
|
var factor = await db.AccountAuthFactors.FindAsync(request.FactorId);
|
||||||
if (factor is null) return NotFound("Auth factor was not found.");
|
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.StepRemain == 0) return challenge;
|
||||||
if (challenge.ExpiredAt.HasValue && challenge.ExpiredAt.Value < Instant.FromDateTimeUtc(DateTime.UtcNow))
|
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))
|
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);
|
challenge.BlacklistFactors.Add(factor.Id);
|
||||||
db.Update(challenge);
|
db.Update(challenge);
|
||||||
als.CreateActionLogFromRequest(ActionLogType.ChallengeSuccess,
|
als.CreateActionLogFromRequest(ActionLogType.ChallengeSuccess,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user