127 lines
4.3 KiB
C#
127 lines
4.3 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public class Authenticator : IAuthenticator
|
|
{
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static Lazy<Authenticator> LazyStatic => new(() => new Authenticator(Options.Create<AuthenticatorParams>(new()), new QRCodeGenerator()));
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static Authenticator Static => LazyStatic.Value;
|
|
|
|
private readonly AuthenticatorParams _params;
|
|
|
|
private readonly QRCodeGenerator _qrCodeGenerator;
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="options"></param>
|
|
/// <param name="qrCodeGenerator"></param>
|
|
public Authenticator(IOptions<AuthenticatorParams> options, QRCodeGenerator qrCodeGenerator)
|
|
{
|
|
_params = options.Value;
|
|
_qrCodeGenerator = qrCodeGenerator;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="length"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="ArgumentException"></exception>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="length"></param>
|
|
/// <returns></returns>
|
|
public string GenerateTotpSecretKey(int? length = null)
|
|
=> Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(length ?? _params.DefaultTotpSecretKeyLength));
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="userEmail"></param>
|
|
/// <param name="secretKey"></param>
|
|
/// <param name="issuer"></param>
|
|
/// <param name="totpUrlFormat"></param>
|
|
/// <param name="pixelsPerModule"></param>
|
|
/// <returns></returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="userEmail"></param>
|
|
/// <param name="length"></param>
|
|
/// <param name="issuer"></param>
|
|
/// <param name="totpUrlFormat"></param>
|
|
/// <param name="pixelsPerModule"></param>
|
|
/// <returns></returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="secretKey"></param>
|
|
/// <param name="step"></param>
|
|
/// <returns></returns>
|
|
public string GenerateTotp(string secretKey, int step = 30) => new Totp(Base32Encoding.ToBytes(secretKey), step).ComputeTotp();
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="totpCode"></param>
|
|
/// <param name="secretKey"></param>
|
|
/// <param name="step"></param>
|
|
/// <param name="window"></param>
|
|
/// <returns></returns>
|
|
public bool VerifyTotp(string totpCode, string secretKey, int step = 30, VerificationWindow? window = null)
|
|
=> new Totp(Base32Encoding.ToBytes(secretKey), step).VerifyTotp(totpCode, out _, window);
|
|
} |