Email Verification And Password Reset #8

Merged
Moonbase merged 6 commits from email-verification into master 2025-07-27 13:55:37 -07:00
6 changed files with 107 additions and 12 deletions
Showing only changes of commit 5abc3d8221 - Show all commits

View File

@ -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;

View File

@ -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<IEmailService, EmailService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ITokenService, TokenService>();
builder.Services.AddScoped<IRoomService, RoomService>();

View File

@ -0,0 +1,75 @@

using MailKit.Net.Smtp;
using MimeKit;
namespace qtc_api.Services.EmailService
{
public class EmailService : IEmailService
{
private IConfiguration _configuration;
private ILogger<EmailService> _logger;
public EmailService(IConfiguration configuration, ILogger<EmailService> logger)
{
_configuration = configuration;
_logger = logger;
}
public async Task SendConfirmationEmail(string email, string name, string confirmUrl)
{
// get config entries
bool confirmationRequired = _configuration.GetValue<bool>("EmailConfig:EmailConfirmationRequired");
string? host = _configuration.GetValue<string>("EmailConfig:SMTPServer");
string? username = _configuration.GetValue<string>("EmailConfig:SMTPUsername");
string? password = _configuration.GetValue<string>("EmailConfig:SMTPPassword");
string? senderAddress = _configuration.GetValue<string>("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);
}
}
}

View File

@ -0,0 +1,7 @@
namespace qtc_api.Services.EmailService
{
public interface IEmailService
{
public Task SendConfirmationEmail(string email, string name, string confirmUrl);
}
}

View File

@ -7,6 +7,13 @@
"GeneralConfig": {
"CDNPath": "./user-content"
},
"EmailConfig": {
"EmailConfirmationRequired": false,
"SMTPServer": "",
"SMTPUsername": "api",
"SMTPPassword": "",
"SMTPSenderAddress": ""
},
"Logging": {
"LogLevel": {
"Default": "Information",

View File

@ -11,23 +11,24 @@
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
<PackageReference Include="MailKit" Version="4.13.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.6" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.12.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.7" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.13.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.22.1" />
<PackageReference Include="MySql.EntityFrameworkCore" Version="9.0.3" />
<PackageReference Include="StackExchange.Redis" Version="2.8.41" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.1" />
<PackageReference Include="MySql.EntityFrameworkCore" Version="9.0.6" />
<PackageReference Include="StackExchange.Redis" Version="2.8.58" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="9.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.12.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.13.0" />
</ItemGroup>
<ItemGroup>