From 831394ae0e57ac3b03df6f3ec583a4779ca1150b Mon Sep 17 00:00:00 2001 From: Mateusz Skoczek Date: Wed, 14 Jan 2026 01:37:46 +0100 Subject: [PATCH] Register endpoint finished --- .../RegisterRequest.cs | 11 ++++---- .../RegisterResponse.cs | 8 +++--- .../API/Endpoints.cs | 27 ++++++++++++++----- .../Commands/Register/RegisterCommand.cs | 5 +++- .../Commands/Register/RegisterHandler.cs | 18 +++++++++++-- .../Commands/Register/RegisterMappers.cs | 9 ++++++- .../{RegisterData.cs => RegisterResult.cs} | 8 +++--- .../Configuration/AccountConfiguration.cs | 6 +---- .../Database/Model/Account.cs | 3 +-- 9 files changed, 64 insertions(+), 31 deletions(-) rename TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/{RegisterData.cs => RegisterResult.cs} (60%) diff --git a/TimetableDesigner.Backend.Services.Authentication.DTO/TimetableDesigner.Backend.Services.Authentication.DTO.API/RegisterRequest.cs b/TimetableDesigner.Backend.Services.Authentication.DTO/TimetableDesigner.Backend.Services.Authentication.DTO.API/RegisterRequest.cs index 2047f1f..cc6973d 100644 --- a/TimetableDesigner.Backend.Services.Authentication.DTO/TimetableDesigner.Backend.Services.Authentication.DTO.API/RegisterRequest.cs +++ b/TimetableDesigner.Backend.Services.Authentication.DTO/TimetableDesigner.Backend.Services.Authentication.DTO.API/RegisterRequest.cs @@ -1,8 +1,7 @@ namespace TimetableDesigner.Backend.Services.Authentication.DTO.API; -public class RegisterRequest -{ - public string Email { get; set; } = null!; - public string Password { get; set; } = null!; - public string PasswordConfirmation { get; set; } = null!; -} \ No newline at end of file +public record RegisterRequest( + string Email, + string Password, + string PasswordConfirmation +); \ No newline at end of file diff --git a/TimetableDesigner.Backend.Services.Authentication.DTO/TimetableDesigner.Backend.Services.Authentication.DTO.API/RegisterResponse.cs b/TimetableDesigner.Backend.Services.Authentication.DTO/TimetableDesigner.Backend.Services.Authentication.DTO.API/RegisterResponse.cs index 09726b9..3001dd5 100644 --- a/TimetableDesigner.Backend.Services.Authentication.DTO/TimetableDesigner.Backend.Services.Authentication.DTO.API/RegisterResponse.cs +++ b/TimetableDesigner.Backend.Services.Authentication.DTO/TimetableDesigner.Backend.Services.Authentication.DTO.API/RegisterResponse.cs @@ -1,6 +1,6 @@ namespace TimetableDesigner.Backend.Services.Authentication.DTO.API; -public class RegisterResponse -{ - -} \ No newline at end of file +public record RegisterResponse( + long Id, + string Email +); \ No newline at end of file diff --git a/TimetableDesigner.Backend.Services.Authentication/API/Endpoints.cs b/TimetableDesigner.Backend.Services.Authentication/API/Endpoints.cs index 9051499..2d97ac0 100644 --- a/TimetableDesigner.Backend.Services.Authentication/API/Endpoints.cs +++ b/TimetableDesigner.Backend.Services.Authentication/API/Endpoints.cs @@ -1,5 +1,8 @@ -using MediatR; +using FluentValidation; +using FluentValidation.Results; +using MediatR; using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; using TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register; using TimetableDesigner.Backend.Services.Authentication.DTO.API; @@ -10,6 +13,10 @@ public static class Endpoints public static IEndpointRouteBuilder MapEndpoints(this IEndpointRouteBuilder app) { app.MapPost("/register", Register) + .AllowAnonymous() + .Produces(201) + .Produces(400) + .Produces(500) .WithName("Register"); app.MapPost("/authenticate_password", AuthenticatePassword) .WithName("AuthenticatePassword"); @@ -19,19 +26,27 @@ public static class Endpoints return app; } - public static async Task, InternalServerError>> Register(RegisterRequest request, CancellationToken cancellationToken, IMediator mediator) + private static async Task, ValidationProblem>> Register(IMediator mediator, IValidator validator, RegisterRequest request) { + ValidationResult validationResult = await validator.ValidateAsync(request); + if (!validationResult.IsValid) + { + return TypedResults.ValidationProblem(validationResult.ToDictionary()); + } + RegisterCommand registerCommand = request.ToCommand(); - RegisterData data = await mediator.Send(registerCommand, cancellationToken); - return Results.Created($"accounts/0", null); + RegisterResult result = await mediator.Send(registerCommand); + RegisterResponse response = result.ToResponse(); + + return TypedResults.Created($"accounts/{response.Id}", response); } - public static async Task, ProblemHttpResult>> AuthenticatePassword(AuthenticatePasswordRequest request, CancellationToken cancellationToken) + public static async Task, ProblemHttpResult>> AuthenticatePassword(AuthenticatePasswordRequest request) { return null; } - public static async Task, ProblemHttpResult>> AuthenticateToken(AuthenticateTokenRequest request, CancellationToken cancellationToken) + public static async Task, ProblemHttpResult>> AuthenticateToken(AuthenticateTokenRequest request) { return null; } diff --git a/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterCommand.cs b/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterCommand.cs index c6e47c5..4354ba3 100644 --- a/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterCommand.cs +++ b/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterCommand.cs @@ -2,4 +2,7 @@ namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register; -public record RegisterCommand(string Email, string Password) : IRequest; \ No newline at end of file +public record RegisterCommand( + string Email, + string Password +) : IRequest; \ No newline at end of file diff --git a/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterHandler.cs b/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterHandler.cs index 30fe67d..3021ec4 100644 --- a/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterHandler.cs +++ b/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterHandler.cs @@ -1,10 +1,11 @@ using MediatR; using TimetableDesigner.Backend.Services.Authentication.Application.Helpers; using TimetableDesigner.Backend.Services.Authentication.Database; +using TimetableDesigner.Backend.Services.Authentication.Database.Model; namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register; -public class RegisterHandler : IRequestHandler +public class RegisterHandler : IRequestHandler { private readonly DatabaseContext _databaseContext; private readonly IPasswordHasher _passwordHasher; @@ -15,9 +16,22 @@ public class RegisterHandler : IRequestHandler _passwordHasher = passwordHasher; } - public async Task Handle(RegisterCommand command, CancellationToken cancellationToken) + public async Task Handle(RegisterCommand command, CancellationToken cancellationToken) { PasswordHashData hash = _passwordHasher.CreateHash(command.Password); + Account account = new Account + { + Email = command.Email, + Password = hash.Hash, + PasswordSalt = hash.Salt, + }; + await _databaseContext.Accounts.AddAsync(account, cancellationToken); + await _databaseContext.SaveChangesAsync(cancellationToken); + + // Publish event (probably in transaction) + + RegisterResult result = account.ToResult(); + return result; } } \ No newline at end of file diff --git a/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterMappers.cs b/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterMappers.cs index 8d0b018..28f95ae 100644 --- a/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterMappers.cs +++ b/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterMappers.cs @@ -1,4 +1,5 @@ -using TimetableDesigner.Backend.Services.Authentication.DTO.API; +using TimetableDesigner.Backend.Services.Authentication.Database.Model; +using TimetableDesigner.Backend.Services.Authentication.DTO.API; namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register; @@ -6,4 +7,10 @@ public static class RegisterMappers { public static RegisterCommand ToCommand(this RegisterRequest request) => new RegisterCommand(request.Email, request.Password); + + public static RegisterResult ToResult(this Account account) => + new RegisterResult(account.Id, account.Email); + + public static RegisterResponse ToResponse(this RegisterResult result) => + new RegisterResponse(result.Id, result.Email); } \ No newline at end of file diff --git a/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterData.cs b/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterResult.cs similarity index 60% rename from TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterData.cs rename to TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterResult.cs index 6afa321..411b422 100644 --- a/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterData.cs +++ b/TimetableDesigner.Backend.Services.Authentication/Application/Commands/Register/RegisterResult.cs @@ -1,6 +1,6 @@ namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register; -public class RegisterData -{ - -} \ No newline at end of file +public record RegisterResult( + long Id, + string Email +); \ No newline at end of file diff --git a/TimetableDesigner.Backend.Services.Authentication/Database/Configuration/AccountConfiguration.cs b/TimetableDesigner.Backend.Services.Authentication/Database/Configuration/AccountConfiguration.cs index 9063258..bfdb0eb 100644 --- a/TimetableDesigner.Backend.Services.Authentication/Database/Configuration/AccountConfiguration.cs +++ b/TimetableDesigner.Backend.Services.Authentication/Database/Configuration/AccountConfiguration.cs @@ -23,11 +23,7 @@ public class AccountConfiguration : IEntityTypeConfiguration .HasMaxLength(1000) .IsRequired(); - builder.Property(x => x.PasswordSaltLeft) - .HasMaxLength(20) - .IsRequired(); - - builder.Property(x => x.PasswordSaltRight) + builder.Property(x => x.PasswordSalt) .HasMaxLength(20) .IsRequired(); diff --git a/TimetableDesigner.Backend.Services.Authentication/Database/Model/Account.cs b/TimetableDesigner.Backend.Services.Authentication/Database/Model/Account.cs index 827e7f1..afd734a 100644 --- a/TimetableDesigner.Backend.Services.Authentication/Database/Model/Account.cs +++ b/TimetableDesigner.Backend.Services.Authentication/Database/Model/Account.cs @@ -5,8 +5,7 @@ public class Account public long Id { get; set; } public string Email { get; set; } = null!; public byte[] Password { get; set; } = null!; - public string PasswordSaltLeft { get; set; } = null!; - public string PasswordSaltRight { get; set; } = null!; + public string PasswordSalt { get; set; } = null!; public uint Version { get; set; } public virtual IEnumerable RefreshTokens { get; set; } = new List();