main project split, authpassword endpoint created
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
using MediatR;
|
||||
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Commands.AuthPassword;
|
||||
|
||||
public record AuthPasswordCommand
|
||||
(
|
||||
string Email,
|
||||
string Password,
|
||||
bool RememberMe
|
||||
)
|
||||
: IRequest<AuthPasswordResult>;
|
||||
@@ -0,0 +1,36 @@
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
||||
using TimetableDesigner.Backend.Services.Authentication.Database;
|
||||
using TimetableDesigner.Backend.Services.Authentication.Database.Model;
|
||||
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Commands.AuthPassword;
|
||||
|
||||
public class AuthPasswordHandler : IRequestHandler<AuthPasswordCommand, AuthPasswordResult>
|
||||
{
|
||||
private readonly DatabaseContext _databaseContext;
|
||||
private readonly IPasswordHasher _passwordHasher;
|
||||
|
||||
public AuthPasswordHandler(DatabaseContext databaseContext, IPasswordHasher passwordHasher)
|
||||
{
|
||||
_databaseContext = databaseContext;
|
||||
_passwordHasher = passwordHasher;
|
||||
}
|
||||
|
||||
public async Task<AuthPasswordResult> Handle(AuthPasswordCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
Account? account = await _databaseContext.Accounts.FirstOrDefaultAsync(x => x.Email == request.Email, cancellationToken);
|
||||
if (account is null)
|
||||
{
|
||||
return AuthPasswordResult.Failure();
|
||||
}
|
||||
|
||||
PasswordHashData hash = new PasswordHashData(account.Password, account.PasswordSalt);
|
||||
if (!_passwordHasher.ValidatePassword(hash, request.Password))
|
||||
{
|
||||
return AuthPasswordResult.Failure();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Commands.AuthPassword;
|
||||
|
||||
public record AuthPasswordResult
|
||||
{
|
||||
public bool IsSuccess { get; }
|
||||
public string? AccessToken { get; }
|
||||
public string? RefreshToken { get; }
|
||||
|
||||
private AuthPasswordResult(bool isSuccess, string? accessToken, string? refreshToken)
|
||||
{
|
||||
IsSuccess = isSuccess;
|
||||
AccessToken = accessToken;
|
||||
RefreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public static AuthPasswordResult Success(string accessToken, string refreshToken) =>
|
||||
new AuthPasswordResult(true, accessToken, refreshToken);
|
||||
|
||||
public static AuthPasswordResult Failure() =>
|
||||
new AuthPasswordResult(false, null, null);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using MediatR;
|
||||
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Commands.Register;
|
||||
|
||||
public record RegisterCommand
|
||||
(
|
||||
string Email,
|
||||
string Password
|
||||
)
|
||||
: IRequest<RegisterResult>;
|
||||
@@ -0,0 +1,39 @@
|
||||
using MediatR;
|
||||
using TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
||||
using TimetableDesigner.Backend.Services.Authentication.Database;
|
||||
using TimetableDesigner.Backend.Services.Authentication.Database.Model;
|
||||
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Commands.Register;
|
||||
|
||||
public class RegisterHandler : IRequestHandler<RegisterCommand, RegisterResult>
|
||||
{
|
||||
private readonly DatabaseContext _databaseContext;
|
||||
private readonly IPasswordHasher _passwordHasher;
|
||||
|
||||
public RegisterHandler(DatabaseContext databaseContext, IPasswordHasher passwordHasher)
|
||||
{
|
||||
_databaseContext = databaseContext;
|
||||
_passwordHasher = passwordHasher;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Change to outbox pattern
|
||||
//RegisterEvent eventData = account.ToEvent();
|
||||
//await _eventQueuePublisher.PublishAsync(eventData);
|
||||
|
||||
await _databaseContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return new RegisterResult(account.Id, account.Email);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Commands.Register;
|
||||
|
||||
public record RegisterResult
|
||||
(
|
||||
long Id,
|
||||
string Email
|
||||
);
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
||||
|
||||
public interface IPasswordHasher
|
||||
{
|
||||
PasswordHashData CreateHash(string password);
|
||||
bool ValidatePassword(PasswordHashData hash, string password);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
||||
|
||||
public record PasswordHashData(
|
||||
byte[] Hash,
|
||||
string Salt
|
||||
);
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Konscious.Security.Cryptography;
|
||||
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
|
||||
|
||||
public class TokenGenerator
|
||||
{
|
||||
/*
|
||||
public TokenGenerator(IConfiguration configuration, DatabaseContext databaseContext)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string GenerateAccessToken(Account account)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> GenerateRefreshTokenAsync(Account account)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task<string> ExtendRefreshTokenAsync()
|
||||
{
|
||||
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TimetableDesigner.Backend.Services.Authentication.Database\TimetableDesigner.Backend.Services.Authentication.Database.csproj" />
|
||||
<ProjectReference Include="..\TimetableDesigner.Backend.Services.Authentication.DTO.Events\TimetableDesigner.Backend.Services.Authentication.DTO.Events.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
|
||||
<PackageReference Include="MediatR" Version="14.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user