Register endpoint finished
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
namespace TimetableDesigner.Backend.Services.Authentication.DTO.API;
|
namespace TimetableDesigner.Backend.Services.Authentication.DTO.API;
|
||||||
|
|
||||||
public class RegisterRequest
|
public record RegisterRequest(
|
||||||
{
|
string Email,
|
||||||
public string Email { get; set; } = null!;
|
string Password,
|
||||||
public string Password { get; set; } = null!;
|
string PasswordConfirmation
|
||||||
public string PasswordConfirmation { get; set; } = null!;
|
);
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace TimetableDesigner.Backend.Services.Authentication.DTO.API;
|
namespace TimetableDesigner.Backend.Services.Authentication.DTO.API;
|
||||||
|
|
||||||
public class RegisterResponse
|
public record RegisterResponse(
|
||||||
{
|
long Id,
|
||||||
|
string Email
|
||||||
}
|
);
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
using MediatR;
|
using FluentValidation;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Http.HttpResults;
|
using Microsoft.AspNetCore.Http.HttpResults;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
|
using TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
|
||||||
using TimetableDesigner.Backend.Services.Authentication.DTO.API;
|
using TimetableDesigner.Backend.Services.Authentication.DTO.API;
|
||||||
|
|
||||||
@@ -10,6 +13,10 @@ public static class Endpoints
|
|||||||
public static IEndpointRouteBuilder MapEndpoints(this IEndpointRouteBuilder app)
|
public static IEndpointRouteBuilder MapEndpoints(this IEndpointRouteBuilder app)
|
||||||
{
|
{
|
||||||
app.MapPost("/register", Register)
|
app.MapPost("/register", Register)
|
||||||
|
.AllowAnonymous()
|
||||||
|
.Produces<RegisterResponse>(201)
|
||||||
|
.Produces<HttpValidationProblemDetails>(400)
|
||||||
|
.Produces(500)
|
||||||
.WithName("Register");
|
.WithName("Register");
|
||||||
app.MapPost("/authenticate_password", AuthenticatePassword)
|
app.MapPost("/authenticate_password", AuthenticatePassword)
|
||||||
.WithName("AuthenticatePassword");
|
.WithName("AuthenticatePassword");
|
||||||
@@ -19,19 +26,27 @@ public static class Endpoints
|
|||||||
return app;
|
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();
|
RegisterCommand registerCommand = request.ToCommand();
|
||||||
RegisterData data = await mediator.Send(registerCommand, cancellationToken);
|
RegisterResult result = await mediator.Send(registerCommand);
|
||||||
return Results.Created<RegisterResponse>($"accounts/0", null);
|
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;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,7 @@
|
|||||||
|
|
||||||
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
|
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>;
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using MediatR;
|
using MediatR;
|
||||||
using TimetableDesigner.Backend.Services.Authentication.Application.Helpers;
|
using TimetableDesigner.Backend.Services.Authentication.Application.Helpers;
|
||||||
using TimetableDesigner.Backend.Services.Authentication.Database;
|
using TimetableDesigner.Backend.Services.Authentication.Database;
|
||||||
|
using TimetableDesigner.Backend.Services.Authentication.Database.Model;
|
||||||
|
|
||||||
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
|
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 DatabaseContext _databaseContext;
|
||||||
private readonly IPasswordHasher _passwordHasher;
|
private readonly IPasswordHasher _passwordHasher;
|
||||||
@@ -15,9 +16,22 @@ public class RegisterHandler : IRequestHandler<RegisterCommand, RegisterData>
|
|||||||
_passwordHasher = passwordHasher;
|
_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);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
|
||||||
|
|
||||||
@@ -6,4 +7,10 @@ public static class RegisterMappers
|
|||||||
{
|
{
|
||||||
public static RegisterCommand ToCommand(this RegisterRequest request) =>
|
public static RegisterCommand ToCommand(this RegisterRequest request) =>
|
||||||
new RegisterCommand(request.Email, request.Password);
|
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);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
|
namespace TimetableDesigner.Backend.Services.Authentication.Application.Commands.Register;
|
||||||
|
|
||||||
public class RegisterData
|
public record RegisterResult(
|
||||||
{
|
long Id,
|
||||||
|
string Email
|
||||||
}
|
);
|
||||||
@@ -23,11 +23,7 @@ public class AccountConfiguration : IEntityTypeConfiguration<Account>
|
|||||||
.HasMaxLength(1000)
|
.HasMaxLength(1000)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
builder.Property(x => x.PasswordSaltLeft)
|
builder.Property(x => x.PasswordSalt)
|
||||||
.HasMaxLength(20)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
builder.Property(x => x.PasswordSaltRight)
|
|
||||||
.HasMaxLength(20)
|
.HasMaxLength(20)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ public class Account
|
|||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
public string Email { get; set; } = null!;
|
public string Email { get; set; } = null!;
|
||||||
public byte[] Password { get; set; } = null!;
|
public byte[] Password { get; set; } = null!;
|
||||||
public string PasswordSaltLeft { get; set; } = null!;
|
public string PasswordSalt { get; set; } = null!;
|
||||||
public string PasswordSaltRight { get; set; } = null!;
|
|
||||||
public uint Version { get; set; }
|
public uint Version { get; set; }
|
||||||
|
|
||||||
public virtual IEnumerable<RefreshToken> RefreshTokens { get; set; } = new List<RefreshToken>();
|
public virtual IEnumerable<RefreshToken> RefreshTokens { get; set; } = new List<RefreshToken>();
|
||||||
|
|||||||
Reference in New Issue
Block a user