From 5abc3d822135f380b706d672d6b56d15f44c2bc3 Mon Sep 17 00:00:00 2001 From: Moonbase Date: Sat, 26 Jul 2025 13:09:19 -0700 Subject: [PATCH] Initial Implementation Of Email Verification --- qtc-net-server/Models/User.cs | 1 + qtc-net-server/Program.cs | 4 + .../Services/EmailService/EmailService.cs | 75 +++++++++++++++++++ .../Services/EmailService/IEmailService.cs | 7 ++ qtc-net-server/appsettings.json | 7 ++ qtc-net-server/qtc-net-server.csproj | 25 ++++--- 6 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 qtc-net-server/Services/EmailService/EmailService.cs create mode 100644 qtc-net-server/Services/EmailService/IEmailService.cs diff --git a/qtc-net-server/Models/User.cs b/qtc-net-server/Models/User.cs index 04f39e0..acd63e9 100644 --- a/qtc-net-server/Models/User.cs +++ b/qtc-net-server/Models/User.cs @@ -9,6 +9,7 @@ public string Role { get; set; } = string.Empty; public string PasswordHash { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; + public bool IsEmailVerified { get; set; } = false; public DateTime DateOfBirth { get; set; } public DateTime CreatedAt { get; set; } public int Status { get; set; } = 0; diff --git a/qtc-net-server/Program.cs b/qtc-net-server/Program.cs index 3490c2b..9ff283a 100644 --- a/qtc-net-server/Program.cs +++ b/qtc-net-server/Program.cs @@ -17,6 +17,7 @@ using qtc_api.Services.ContactService; using qtc_api.Services.CurrencyGamesService; using qtc_api.Services.GameRoomService; using qtc_api.Services.StoreService; +using qtc_api.Services.EmailService; var builder = WebApplication.CreateBuilder(args); @@ -55,6 +56,9 @@ builder.Services.AddAuthentication().AddJwtBearer(options => options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)); else throw new Exception("Cannot Find Environment Variables 'JWT_KEY'. Please Check Environment."); }); + +builder.Services.AddTransient(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/qtc-net-server/Services/EmailService/EmailService.cs b/qtc-net-server/Services/EmailService/EmailService.cs new file mode 100644 index 0000000..2df077f --- /dev/null +++ b/qtc-net-server/Services/EmailService/EmailService.cs @@ -0,0 +1,75 @@ + +using MailKit.Net.Smtp; +using MimeKit; + +namespace qtc_api.Services.EmailService +{ + public class EmailService : IEmailService + { + private IConfiguration _configuration; + private ILogger _logger; + public EmailService(IConfiguration configuration, ILogger logger) + { + _configuration = configuration; + _logger = logger; + } + + public async Task SendConfirmationEmail(string email, string name, string confirmUrl) + { + // get config entries + bool confirmationRequired = _configuration.GetValue("EmailConfig:EmailConfirmationRequired"); + string? host = _configuration.GetValue("EmailConfig:SMTPServer"); + string? username = _configuration.GetValue("EmailConfig:SMTPUsername"); + string? password = _configuration.GetValue("EmailConfig:SMTPPassword"); + string? senderAddress = _configuration.GetValue("EmailConfig:SMTPSenderAddress"); + + if (!confirmationRequired) + { + _logger.LogInformation("Email Confirmation Is Disabled. For Better Security Of Your Instance, Consider Setting Up An Email Sender Service."); + return; + } else if (host == null || username == null || password == null || senderAddress == null) + { + _logger.LogInformation("Email Confirmation Is Enabled But No SMTP Settings Were Set."); + return; + } + + // set email subject + string emailSubject = "QtC.NET Email Confirmation"; + + // build confirmation email body + StringBuilder emailBody = new StringBuilder(); + emailBody.AppendLine("Hello! This email was used to create an account on a QtC.NET Server."); + emailBody.AppendLine(); + emailBody.AppendLine($"You can confirm your email by clicking here - {confirmUrl}"); + emailBody.AppendLine(); + emailBody.AppendLine("If you did not create a QtC.NET account on any server, you may simply ignore this email."); + + // create new client + using var client = new SmtpClient() + { + RequireTLS = true + }; + + // connect and authenticate + await client.ConnectAsync(host, 587, true); + await client.AuthenticateAsync(username, password); + + // construct email + using var message = new MimeMessage(); + message.To.Add(new MailboxAddress(name, email)); + message.From.Add(new MailboxAddress("QtC.NET Server", senderAddress)); + + message.Subject = emailSubject; + message.Body = new TextPart(MimeKit.Text.TextFormat.Plain) + { + Text = emailBody.ToString() + }; + + // send email + await client.SendAsync(message); + + // disconnect + await client.DisconnectAsync(true); + } + } +} diff --git a/qtc-net-server/Services/EmailService/IEmailService.cs b/qtc-net-server/Services/EmailService/IEmailService.cs new file mode 100644 index 0000000..fd4d83e --- /dev/null +++ b/qtc-net-server/Services/EmailService/IEmailService.cs @@ -0,0 +1,7 @@ +namespace qtc_api.Services.EmailService +{ + public interface IEmailService + { + public Task SendConfirmationEmail(string email, string name, string confirmUrl); + } +} diff --git a/qtc-net-server/appsettings.json b/qtc-net-server/appsettings.json index 935f72c..3fe0b91 100644 --- a/qtc-net-server/appsettings.json +++ b/qtc-net-server/appsettings.json @@ -7,6 +7,13 @@ "GeneralConfig": { "CDNPath": "./user-content" }, + "EmailConfig": { + "EmailConfirmationRequired": false, + "SMTPServer": "", + "SMTPUsername": "api", + "SMTPPassword": "", + "SMTPSenderAddress": "" + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/qtc-net-server/qtc-net-server.csproj b/qtc-net-server/qtc-net-server.csproj index 6488c48..e80f137 100644 --- a/qtc-net-server/qtc-net-server.csproj +++ b/qtc-net-server/qtc-net-server.csproj @@ -11,23 +11,24 @@ - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + - - - + + + - +