using System.Security.Cryptography; using System.Text; using LanguageExt.Common; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using W542.GandalfReborn.Commands; using W542.GandalfReborn.Data.Database.Repositories; using W542.GandalfReborn.Data.Dto; using W542.GandalfReborn.Extensions; using W542.GandalfReborn.Handlers.Commands; namespace W542.GandalfReborn.Handlers.Security; public class GetTokensCommandHandler(IAuthCodeRepository authCodeRepository, TimeProvider timeProvider, IMediator mediator) : IGrCommandHandler> { public async Task> Handle(GetTokensCommand command, CancellationToken cancellationToken) { var authCode = await authCodeRepository .Query(q => q.Where(x => x.Code == command.AuthCode)) .SingleOrDefaultAsync(cancellationToken); if (authCode is null) { return "Auth code could not be found.".AsErrorResult(); } if (authCode.Expiration <= timeProvider.GetUtcNow()) { await authCodeRepository .Query(q => q.Where(x => x.Id == authCode.Id)) .ExecuteDeleteAsync(cancellationToken); return "Auth code is expired.".AsErrorResult(); } switch (authCode.Algorithm) { case "S256": var proofKeyBytes = Encoding.UTF8.GetBytes(command.ProofKey); var sha256Bytes = SHA256.HashData(proofKeyBytes); var hexProofKey = BitConverter.ToString(sha256Bytes).Replace("-", string.Empty).ToLower(); if (authCode.Challenge != hexProofKey) return "Code challenge failed.".AsErrorResult(); break; default: return $"Algorithm '{authCode.Algorithm}' not supported".AsErrorResult(); } await authCodeRepository .Query(q => q.Where(x => x.Id == authCode.Id)) .ExecuteDeleteAsync(cancellationToken); return await mediator.Send(new CreateTokensCommand {SubjectId = authCode.SubjectId}, cancellationToken); } }