Register endpoint finished

This commit is contained in:
2026-01-14 01:37:46 +01:00
Unverified
parent 2b9ddef055
commit 831394ae0e
9 changed files with 64 additions and 31 deletions

View File

@@ -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!;
}
public record RegisterRequest(
string Email,
string Password,
string PasswordConfirmation
);

View File

@@ -1,6 +1,6 @@
namespace TimetableDesigner.Backend.Services.Authentication.DTO.API;
public class RegisterResponse
{
}
public record RegisterResponse(
long Id,
string Email
);

View File

@@ -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<RegisterResponse>(201)
.Produces<HttpValidationProblemDetails>(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<Results<Created<RegisterResponse>, InternalServerError>> Register(RegisterRequest request, CancellationToken cancellationToken, IMediator mediator)
private static async Task<Results<Created<RegisterResponse>, ValidationProblem>> Register(IMediator mediator, IValidator<RegisterRequest> 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<RegisterResponse>($"accounts/0", null);
RegisterResult result = await mediator.Send(registerCommand);
RegisterResponse response = result.ToResponse();
return TypedResults.Created($"accounts/{response.Id}", response);
}
public static async Task<Results<Ok<AuthenticateResponse>, ProblemHttpResult>> AuthenticatePassword(AuthenticatePasswordRequest request, CancellationToken cancellationToken)
public static async Task<Results<Ok<AuthenticateResponse>, ProblemHttpResult>> AuthenticatePassword(AuthenticatePasswordRequest request)
{
return null;
}
public static async Task<Results<Ok<AuthenticateResponse>, ProblemHttpResult>> AuthenticateToken(AuthenticateTokenRequest request, CancellationToken cancellationToken)
public static async Task<Results<Ok<AuthenticateResponse>, ProblemHttpResult>> AuthenticateToken(AuthenticateTokenRequest request)
{
return null;
}

View File

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

View File

@@ -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<RegisterCommand, RegisterData>
public class RegisterHandler : IRequestHandler<RegisterCommand, RegisterResult>
{
private readonly DatabaseContext _databaseContext;
private readonly IPasswordHasher _passwordHasher;
@@ -15,9 +16,22 @@ public class RegisterHandler : IRequestHandler<RegisterCommand, RegisterData>
_passwordHasher = passwordHasher;
}
public async Task<RegisterData> Handle(RegisterCommand command, CancellationToken cancellationToken)
public async Task<RegisterResult> 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;
}
}

View File

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

View File

@@ -1,6 +1,6 @@
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
public class RegisterData
{
}
public record RegisterResult(
long Id,
string Email
);

View File

@@ -23,11 +23,7 @@ public class AccountConfiguration : IEntityTypeConfiguration<Account>
.HasMaxLength(1000)
.IsRequired();
builder.Property(x => x.PasswordSaltLeft)
.HasMaxLength(20)
.IsRequired();
builder.Property(x => x.PasswordSaltRight)
builder.Property(x => x.PasswordSalt)
.HasMaxLength(20)
.IsRequired();

View File

@@ -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<RefreshToken> RefreshTokens { get; set; } = new List<RefreshToken>();