✨ Auth factors APIs
This commit is contained in:
@ -2,6 +2,7 @@ using DysonNetwork.Sphere.Storage;
|
||||
using EFCore.BulkExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using OtpNet;
|
||||
|
||||
namespace DysonNetwork.Sphere.Account;
|
||||
|
||||
@ -64,6 +65,91 @@ public class AccountService(
|
||||
await spells.NotifyMagicSpell(spell);
|
||||
}
|
||||
|
||||
public async Task<bool> CheckAuthFactorExists(Account account, AccountAuthFactorType type)
|
||||
{
|
||||
var isExists = await db.AccountAuthFactors
|
||||
.Where(x => x.AccountId == account.Id && x.Type == type)
|
||||
.AnyAsync();
|
||||
return isExists;
|
||||
}
|
||||
|
||||
public async Task<AccountAuthFactor?> CreateAuthFactor(Account account, AccountAuthFactorType type, string? secret)
|
||||
{
|
||||
AccountAuthFactor? factor = null;
|
||||
switch (type)
|
||||
{
|
||||
case AccountAuthFactorType.Password:
|
||||
if (string.IsNullOrWhiteSpace(secret)) throw new ArgumentNullException(nameof(secret));
|
||||
factor = new AccountAuthFactor
|
||||
{
|
||||
Type = AccountAuthFactorType.Password,
|
||||
Trustworthy = 1,
|
||||
AccountId = account.Id,
|
||||
Secret = secret,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant(),
|
||||
}.HashSecret();
|
||||
break;
|
||||
case AccountAuthFactorType.EmailCode:
|
||||
factor = new AccountAuthFactor
|
||||
{
|
||||
Type = AccountAuthFactorType.EmailCode,
|
||||
Trustworthy = 2,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant(),
|
||||
};
|
||||
break;
|
||||
case AccountAuthFactorType.InAppCode:
|
||||
factor = new AccountAuthFactor
|
||||
{
|
||||
Type = AccountAuthFactorType.InAppCode,
|
||||
Trustworthy = 1,
|
||||
EnabledAt = SystemClock.Instance.GetCurrentInstant()
|
||||
};
|
||||
break;
|
||||
case AccountAuthFactorType.TimedCode:
|
||||
var skOtp = KeyGeneration.GenerateRandomKey(20);
|
||||
var skOtp32 = Base32Encoding.ToString(skOtp);
|
||||
factor = new AccountAuthFactor
|
||||
{
|
||||
Secret = skOtp32,
|
||||
Type = AccountAuthFactorType.InAppCode,
|
||||
Trustworthy = 2,
|
||||
EnabledAt = null, // It needs to be tired once to enable
|
||||
CreatedResponse = new Dictionary<string, object>
|
||||
{
|
||||
["uri"] = new OtpUri(
|
||||
OtpType.Totp,
|
||||
skOtp32,
|
||||
account.Id.ToString(),
|
||||
"Solar Network"
|
||||
).ToString(),
|
||||
}
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
|
||||
if (factor is null) throw new InvalidOperationException("Unable to create auth factor.");
|
||||
db.AccountAuthFactors.Add(factor);
|
||||
await db.SaveChangesAsync();
|
||||
return factor;
|
||||
}
|
||||
|
||||
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."
|
||||
);
|
||||
|
||||
factor.EnabledAt = SystemClock.Instance.GetCurrentInstant();
|
||||
db.Update(factor);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
/// Maintenance methods for server administrator
|
||||
public async Task EnsureAccountProfileCreated()
|
||||
{
|
||||
|
Reference in New Issue
Block a user