2022-03-02 11:37:15 +00:00
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Mail;
|
|
|
|
|
using VoidCat.Model;
|
|
|
|
|
using VoidCat.Services.Abstractions;
|
|
|
|
|
|
|
|
|
|
namespace VoidCat.Services.Users;
|
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public abstract class BaseEmailVerification : IEmailVerification
|
2022-03-02 11:37:15 +00:00
|
|
|
|
{
|
2022-06-08 16:17:53 +00:00
|
|
|
|
public const int HoursExpire = 1;
|
2022-03-02 11:37:15 +00:00
|
|
|
|
private readonly VoidSettings _settings;
|
2022-06-08 16:17:53 +00:00
|
|
|
|
private readonly ILogger<BaseEmailVerification> _logger;
|
2022-03-02 11:37:15 +00:00
|
|
|
|
private readonly RazorPartialToStringRenderer _renderer;
|
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
|
protected BaseEmailVerification(ILogger<BaseEmailVerification> logger, VoidSettings settings,
|
2022-06-06 21:51:25 +00:00
|
|
|
|
RazorPartialToStringRenderer renderer)
|
2022-03-02 11:37:15 +00:00
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_settings = settings;
|
|
|
|
|
_renderer = renderer;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
|
/// <inheritdoc />
|
2022-03-02 11:37:15 +00:00
|
|
|
|
public async ValueTask<EmailVerificationCode> SendNewCode(PrivateVoidUser user)
|
|
|
|
|
{
|
2022-06-08 16:17:53 +00:00
|
|
|
|
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);
|
2022-06-06 21:51:25 +00:00
|
|
|
|
|
2022-03-02 11:37:15 +00:00
|
|
|
|
// 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);
|
2022-06-06 21:51:25 +00:00
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
|
var msgContent = await _renderer.RenderPartialToStringAsync("~/Pages/EmailCode.cshtml", token);
|
2022-03-02 11:37:15 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
|
return token;
|
2022-03-02 11:37:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
|
/// <inheritdoc />
|
2022-03-02 11:37:15 +00:00
|
|
|
|
public async ValueTask<bool> VerifyCode(PrivateVoidUser user, Guid code)
|
|
|
|
|
{
|
2022-06-08 16:17:53 +00:00
|
|
|
|
var token = await GetToken(user.Id, code);
|
2022-03-02 11:37:15 +00:00
|
|
|
|
if (token == default) return false;
|
2022-06-06 21:51:25 +00:00
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
|
var isValid = user.Id == token.User &&
|
|
|
|
|
DateTime.SpecifyKind(token.Expires, DateTimeKind.Utc) > DateTimeOffset.UtcNow;
|
2022-03-02 11:37:15 +00:00
|
|
|
|
if (isValid)
|
|
|
|
|
{
|
2022-06-08 16:17:53 +00:00
|
|
|
|
await DeleteToken(user.Id, code);
|
2022-03-02 11:37:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return isValid;
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-08 16:17:53 +00:00
|
|
|
|
protected abstract ValueTask SaveToken(EmailVerificationCode code);
|
|
|
|
|
protected abstract ValueTask<EmailVerificationCode?> GetToken(Guid user, Guid code);
|
|
|
|
|
protected abstract ValueTask DeleteToken(Guid user, Guid code);
|
2022-03-02 11:37:15 +00:00
|
|
|
|
}
|