PasswordHasher added

This commit is contained in:
2026-01-13 02:47:34 +01:00
Unverified
parent 2b3650de1a
commit 2b9ddef055
11 changed files with 66 additions and 25 deletions

View File

@@ -19,18 +19,19 @@ public static class Endpoints
return app;
}
public static async Task<Results<Created<RegisterResponse>, ProblemHttpResult>> Register(RegisterCommand command, IMediator mediator)
public static async Task<Results<Created<RegisterResponse>, InternalServerError>> Register(RegisterRequest request, CancellationToken cancellationToken, IMediator mediator)
{
await mediator.Send(command);
return Results.Created($"accounts/0", null);
RegisterCommand registerCommand = request.ToCommand();
RegisterData data = await mediator.Send(registerCommand, cancellationToken);
return Results.Created<RegisterResponse>($"accounts/0", null);
}
public static async Task<Results<Ok<AuthenticateResponse>, ProblemHttpResult>> AuthenticatePassword(AuthenticatePasswordRequest request)
public static async Task<Results<Ok<AuthenticateResponse>, ProblemHttpResult>> AuthenticatePassword(AuthenticatePasswordRequest request, CancellationToken cancellationToken)
{
return null;
}
public static async Task<Results<Ok<AuthenticateResponse>, ProblemHttpResult>> AuthenticateToken(AuthenticateTokenRequest request)
public static async Task<Results<Ok<AuthenticateResponse>, ProblemHttpResult>> AuthenticateToken(AuthenticateTokenRequest request, CancellationToken cancellationToken)
{
return null;
}

View File

@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore;
using TimetableDesigner.Backend.Services.Authentication.Database;
using TimetableDesigner.Backend.Services.Authentication.DTO.API;
namespace TimetableDesigner.Backend.Services.Authentication.DTO.Validators;
namespace TimetableDesigner.Backend.Services.Authentication.API.Validators;
public class RegisterRequestValidator : AbstractValidator<RegisterRequest>
{

View File

@@ -2,4 +2,4 @@
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
public record RegisterCommand(string Email, string Password) : IRequest;
public record RegisterCommand(string Email, string Password) : IRequest<RegisterData>;

View File

@@ -1,6 +1,6 @@
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
public class RegisterDto
public class RegisterData
{
}

View File

@@ -1,19 +1,23 @@
using MediatR;
using TimetableDesigner.Backend.Services.Authentication.Application.Helpers;
using TimetableDesigner.Backend.Services.Authentication.Database;
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
public class RegisterHandler : IRequestHandler<RegisterCommand>
public class RegisterHandler : IRequestHandler<RegisterCommand, RegisterData>
{
private readonly DatabaseContext _databaseContext;
private readonly IPasswordHasher _passwordHasher;
public RegisterHandler(DatabaseContext databaseContext)
public RegisterHandler(DatabaseContext databaseContext, IPasswordHasher passwordHasher)
{
_databaseContext = databaseContext;
_passwordHasher = passwordHasher;
}
public async Task Handle(RegisterCommand command, CancellationToken cancellationToken)
public async Task<RegisterData> Handle(RegisterCommand command, CancellationToken cancellationToken)
{
PasswordHashData hash = _passwordHasher.CreateHash(command.Password);
}
}

View File

@@ -1,7 +1,6 @@
using TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
using TimetableDesigner.Backend.Services.Authentication.DTO.API;
using TimetableDesigner.Backend.Services.Authentication.DTO.API;
namespace TimetableDesigner.Backend.Services.Authentication.DTO.Mappers;
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
public static class RegisterMappers
{

View File

@@ -3,5 +3,5 @@
public interface IPasswordHasher
{
PasswordHashData CreateHash(string password);
byte[] ComputeHash(string password, string salt);
bool ValidatePassword(PasswordHashData hash, string password);
}

View File

@@ -2,6 +2,5 @@
public record PasswordHashData(
byte[] Hash,
string LeftSalt,
string RightSalt
string Salt
);

View File

@@ -1,6 +1,39 @@
namespace TimetableDesigner.Backend.Services.Authentication.Application.Helpers;
using System.Security.Cryptography;
using System.Text;
using Konscious.Security.Cryptography;
public class PasswordHasher
namespace TimetableDesigner.Backend.Services.Authentication.Application.Helpers;
public class PasswordHasher : IPasswordHasher
{
private const string RandomPasswordCharacters = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890!@#$%^&*()-_=+[{]};:'\"\\|,<.>/?";
public PasswordHashData CreateHash(string password)
{
string salt = RandomNumberGenerator.GetString(RandomPasswordCharacters, 20);
byte[] hash = ComputeHash(password, salt);
PasswordHashData data = new PasswordHashData(hash, salt);
return data;
}
public bool ValidatePassword(PasswordHashData hash, string password)
{
byte[] actualHash = hash.Hash;
byte[] checkedHash = ComputeHash(password, hash.Salt);
bool checkResult = checkedHash.SequenceEqual(actualHash);
return checkResult;
}
protected byte[] ComputeHash(string password, string salt)
{
Argon2id hashFunction = new Argon2id(Encoding.UTF8.GetBytes(password))
{
Salt = Encoding.UTF8.GetBytes(salt),
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};
byte[] hash = hashFunction.GetBytes(32);
return hash;
}
}

View File

@@ -3,8 +3,9 @@ using FluentValidation;
using Microsoft.AspNetCore.Identity.Data;
using Microsoft.EntityFrameworkCore;
using TimetableDesigner.Backend.Services.Authentication.API;
using TimetableDesigner.Backend.Services.Authentication.API.Validators;
using TimetableDesigner.Backend.Services.Authentication.Application.Helpers;
using TimetableDesigner.Backend.Services.Authentication.Database;
using TimetableDesigner.Backend.Services.Authentication.DTO.Validators;
namespace TimetableDesigner.Backend.Services.Authentication;
@@ -16,6 +17,7 @@ public static class Program
.SetupOpenApi()
.SetupSecurity()
.SetupDatabase()
.SetupHelpers()
.SetupValidation()
.SetupMediatR()
.Build();
@@ -47,6 +49,12 @@ public static class Program
return builder;
}
private static WebApplicationBuilder SetupHelpers(this WebApplicationBuilder builder)
{
builder.Services.AddSingleton<IPasswordHasher, PasswordHasher>();
return builder;
}
private static WebApplicationBuilder SetupValidation(this WebApplicationBuilder builder)
{
builder.Services.AddScoped<IValidator<TimetableDesigner.Backend.Services.Authentication.DTO.API.RegisterRequest>, RegisterRequestValidator>();

View File

@@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="FluentValidation" Version="12.1.1" />
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
<PackageReference Include="MediatR" Version="14.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.9">
@@ -19,10 +20,6 @@
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup>
<ItemGroup>
<Folder Include="DTO\Mappers\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TimetableDesigner.Backend.Services.Authentication.DTO\TimetableDesigner.Backend.Services.Authentication.DTO.API\TimetableDesigner.Backend.Services.Authentication.DTO.API.csproj" />
</ItemGroup>