gandalf-reborn/Api/Handlers/Security/AuthCodeRequestCommandHandler.cs
2025-03-02 12:51:02 +01:00

69 lines
3.0 KiB
C#

using LanguageExt.Common;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using W542.GandalfReborn.Commands;
using W542.GandalfReborn.Data.Database.Repositories;
using W542.GandalfReborn.Data.Dto;
using W542.GandalfReborn.Data.Entities.Subject.SignIn;
using W542.GandalfReborn.Extensions;
using W542.GandalfReborn.Handlers.Commands;
namespace W542.GandalfReborn.Handlers.Security;
public class AuthCodeRequestCommandHandler(IPasswordHasher<object> passwordHasher, ISubjectRepository subjectRepository, IMediator mediator) : IGrCommandHandler<AuthCodeRequestCommand, Result<AuthCodeDto>>
{
private static readonly object MicrosoftIsAMeme = new();
public async Task<Result<AuthCodeDto>> Handle(AuthCodeRequestCommand command, CancellationToken cancellationToken)
{
var subjectResult = await subjectRepository.GetSingle(query => query
.Where(subject => subject.Name == command.UsernameOrEmail || subject.SignInMethods.Any(signIn => signIn.Method == SignInMethod.Simple && signIn.Email == command.UsernameOrEmail))
.Include(x => x.SignInMethods)
);
if (subjectResult.IsFaulted)
{
return $"User {command.UsernameOrEmail} does not exist.".AsErrorResult<AuthCodeDto>();
}
var subject = subjectResult.GetValue();
var simpleSignIn = subject.SignInMethods.SingleOrDefault(x => x.Method == SignInMethod.Simple);
if (simpleSignIn?.PasswordHash is null)
{
return "User does not support simple password.".AsErrorResult<AuthCodeDto>();
}
var verification = passwordHasher.VerifyHashedPassword(MicrosoftIsAMeme, simpleSignIn.PasswordHash, command.Password);
switch (verification)
{
case PasswordVerificationResult.Failed:
return "Wrong Password.".AsErrorResult<AuthCodeDto>();
case PasswordVerificationResult.Success:
return await mediator.Send(new CreateAuthCodeCommand { SubjectId = subject.Id!.Value, CodeChallenge = command.CodeChallenge, Algorithm = command.Algorithm }, cancellationToken);
case PasswordVerificationResult.SuccessRehashNeeded:
var newHashResult = await mediator.Send(new HashPasswordCommand { RawPassword = command.Password }, cancellationToken);
if (newHashResult.IsFaulted)
{
return newHashResult.AsErrorResult<AuthCodeDto, string>();
}
var newHash = newHashResult.GetValue();
simpleSignIn.PasswordHash = newHash;
await subjectRepository.SaveChanges();
return await mediator.Send(new CreateAuthCodeCommand { SubjectId = subject.Id!.Value, CodeChallenge = command.CodeChallenge, Algorithm = command.Algorithm }, cancellationToken);
default:
return $"Password verification type not supported: {verification}".AsErrorResult<AuthCodeDto>();
}
}
}