using Grpc.Core; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Svrnty.CQRS.Altcha.Abstractions; namespace Svrnty.CQRS.Altcha.Grpc; /// /// Default backed by gRPC. Calls /// AltchaService.CreateChallenge on the configured endpoint and /// projects the response onto . /// public sealed class AltchaGrpcChallengeProvider : IAltchaChallengeProvider { private readonly AltchaService.AltchaServiceClient _client; private readonly IOptions _options; private readonly ILogger _logger; public AltchaGrpcChallengeProvider( AltchaService.AltchaServiceClient client, IOptions options, ILogger logger) { _client = client; _options = options; _logger = logger; } public async Task CreateAsync(CancellationToken cancellationToken = default) { var opts = _options.Value; var metadata = await AltchaCallCredentials.BuildMetadataAsync(opts, cancellationToken); var deadline = DateTime.UtcNow.Add(opts.CallTimeout); try { var response = await _client.CreateChallengeAsync( new CreateChallengeRequest(), headers: metadata, deadline: deadline, cancellationToken: cancellationToken); return new AltchaChallenge { Algorithm = response.Algorithm, Challenge = response.ChallengeHash, Salt = response.Salt, Signature = response.Signature, MaxNumber = response.Maxnumber }; } catch (RpcException ex) { _logger.LogError(ex, "Altcha create-challenge failed against {Endpoint}: {Status}", opts.Endpoint, ex.StatusCode); throw; } } }