using LanguageExt.Common; using MediatR; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Suspectus.Gandalf.Core.Abstractions.CQRS; using Suspectus.Gandalf.Core.Abstractions.Extensions; using Suspectus.Gandalf.Palantir.Api.Commands; using Suspectus.Gandalf.Palantir.Api.Handlers.Commands; using Suspectus.Gandalf.Palantir.Data.Database; using Suspectus.Gandalf.Palantir.Data.Dto; namespace Suspectus.Gandalf.Palantir.Api.Handlers.Security; public class PasswordHashingHandler(ApplicationContext applicationContext, IMediator mediator, IPasswordHasher passwordHasher) : ICommandHandler, ICommandHandler { private static readonly object MicrosoftIsAMeme = new(); public Task> Handle(HashPasswordCommand command, CancellationToken cancellationToken) { try { var hash = passwordHasher.HashPassword(MicrosoftIsAMeme, command.RawPassword); return Task.FromResult(hash.AsResult()); } catch (Exception) { return Task.FromResult("Something went wrong while hashing password.".AsErrorResult()); } } public async Task> Handle(VerifyPasswordCommand request, CancellationToken cancellationToken) { var signIn = await applicationContext.SignIns.SingleOrDefaultAsync(x => x.Id == request.SignInId, cancellationToken: cancellationToken); if (signIn is null) { return "Sign-in method not found.".AsErrorResult(); } if (signIn.PasswordHash is null) { return "Sign-in method does not have a password hash.".AsErrorResult(); } var verification = passwordHasher.VerifyHashedPassword(MicrosoftIsAMeme, signIn.PasswordHash, request.RawPassword); switch (verification) { case PasswordVerificationResult.Failed: return false; case PasswordVerificationResult.Success: return true; case PasswordVerificationResult.SuccessRehashNeeded: var newHashResult = await mediator.Send(new HashPasswordCommand { RawPassword = request.RawPassword }, cancellationToken); if (newHashResult.IsFaulted) { return newHashResult.AsErrorResult(); } var newHash = newHashResult.GetValue(); signIn.PasswordHash = newHash; await applicationContext.SaveChangesAsync(cancellationToken); return true; default: return $"Password verification type not supported: {verification}".AsErrorResult(); } } }