using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Common.Interfaces.Services;
using Microsoft.Extensions.Options;
using OtpNet;
using QRCoder;
using System.Text;
namespace EnvelopeGenerator.Application.Services;
///
///
///
public class Authenticator : IAuthenticator
{
///
///
///
public static Lazy LazyStatic => new(() => new Authenticator(Options.Create(new()), new QRCodeGenerator()));
///
///
///
public static Authenticator Static => LazyStatic.Value;
private readonly AuthenticatorParams _params;
private readonly QRCodeGenerator _qrCodeGenerator;
///
///
///
///
///
public Authenticator(IOptions options, QRCodeGenerator qrCodeGenerator)
{
_params = options.Value;
_qrCodeGenerator = qrCodeGenerator;
}
///
///
///
///
///
///
public string GenerateCode(int length)
{
//TODO: Inject Random as a singleton to support multithreading to improve performance.
Random random = new();
if (length <= 0)
throw new ArgumentException("Password length must be greater than 0.");
var passwordBuilder = new StringBuilder(length);
for (int i = 0; i < length; i++)
passwordBuilder.Append(_params.CharPool[random.Next(_params.CharPool.Length)]);
return passwordBuilder.ToString();
}
///
///
///
///
///
public string GenerateTotpSecretKey(int? length = null)
=> Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(length ?? _params.DefaultTotpSecretKeyLength));
///
///
///
///
///
///
///
///
///
public byte[] GenerateTotpQrCode(string userEmail, string secretKey, string? issuer = null, string? totpUrlFormat = null, int? pixelsPerModule = null)
{
var url = string.Format(totpUrlFormat ?? _params.TotpUrlFormat,
Uri.EscapeDataString(userEmail),
Uri.EscapeDataString(secretKey),
Uri.EscapeDataString(issuer ?? _params.TotpIssuer));
using var qrCodeData = _qrCodeGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q);
using var qrCode = new BitmapByteQRCode(qrCodeData);
return qrCode.GetGraphic(pixelsPerModule ?? _params.TotpQRPixelsPerModule);
}
///
///
///
///
///
///
///
///
///
public byte[] GenerateTotpQrCode(string userEmail, int? length = null, string? issuer = null, string? totpUrlFormat = null, int? pixelsPerModule = null)
{
return GenerateTotpQrCode(
userEmail: userEmail,
secretKey: GenerateTotpSecretKey(length: length),
issuer: issuer,
totpUrlFormat: totpUrlFormat,
pixelsPerModule: pixelsPerModule);
}
///
///
///
///
///
///
public string GenerateTotp(string secretKey, int step = 30) => new Totp(Base32Encoding.ToBytes(secretKey), step).ComputeTotp();
///
///
///
///
///
///
///
///
public bool VerifyTotp(string totpCode, string secretKey, int step = 30, VerificationWindow? window = null)
=> new Totp(Base32Encoding.ToBytes(secretKey), step).VerifyTotp(totpCode, out _, window);
}