using System.Net; using System.Net.Mail; using VoidCat.Model; using VoidCat.Services.Abstractions; namespace VoidCat.Services.Users; /// public abstract class BaseEmailVerification : IEmailVerification { public const int HoursExpire = 1; private readonly VoidSettings _settings; private readonly ILogger _logger; private readonly RazorPartialToStringRenderer _renderer; protected BaseEmailVerification(ILogger logger, VoidSettings settings, RazorPartialToStringRenderer renderer) { _logger = logger; _settings = settings; _renderer = renderer; } /// public async ValueTask SendNewCode(PrivateVoidUser user) { var token = new EmailVerificationCode(user.Id, Guid.NewGuid(), DateTime.UtcNow.AddHours(HoursExpire)); await SaveToken(token); _logger.LogInformation("Saved email verification token for User={Id} Token={Token}", user.Id, token.Code); // send email try { var conf = _settings.Smtp; using var sc = new SmtpClient(); sc.Host = conf?.Server?.Host!; sc.Port = conf?.Server?.Port ?? 25; sc.EnableSsl = conf?.Server?.Scheme == "tls"; sc.Credentials = new NetworkCredential(conf?.Username, conf?.Password); var msgContent = await _renderer.RenderPartialToStringAsync("~/Pages/EmailCode.cshtml", token); var msg = new MailMessage(); msg.From = new MailAddress(conf?.Username ?? "no-reply@void.cat"); msg.To.Add(user.Email); msg.Subject = "Email verification code"; msg.IsBodyHtml = true; msg.Body = msgContent; var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromMinutes(1)); await sc.SendMailAsync(msg, cts.Token); } catch (Exception ex) { _logger.LogError(ex, "Failed to send email verification code {Error}", ex.Message); } return token; } /// public async ValueTask VerifyCode(PrivateVoidUser user, Guid code) { var token = await GetToken(user.Id, code); if (token == default) return false; var isValid = user.Id == token.User && DateTime.SpecifyKind(token.Expires, DateTimeKind.Utc) > DateTimeOffset.UtcNow; if (isValid) { await DeleteToken(user.Id, code); } return isValid; } protected abstract ValueTask SaveToken(EmailVerificationCode code); protected abstract ValueTask GetToken(Guid user, Guid code); protected abstract ValueTask DeleteToken(Guid user, Guid code); }