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.VerifyChallenge on the configured endpoint and
/// maps any failure (transport error, deadline, server-reported failure)
/// to . Verification failures are
/// safe defaults — callers see an Unauthorized outcome from the
/// auth check.
///
public sealed class AltchaGrpcVerifier : IAltchaVerifier
{
private readonly AltchaService.AltchaServiceClient _client;
private readonly IOptions _options;
private readonly ILogger _logger;
public AltchaGrpcVerifier(
AltchaService.AltchaServiceClient client,
IOptions options,
ILogger logger)
{
_client = client;
_options = options;
_logger = logger;
}
public async Task VerifyAsync(string payload, CancellationToken cancellationToken = default)
{
var opts = _options.Value;
try
{
var metadata = await AltchaCallCredentials.BuildMetadataAsync(opts, cancellationToken);
var deadline = DateTime.UtcNow.Add(opts.CallTimeout);
var response = await _client.VerifyChallengeAsync(
new VerifyChallengeRequest { Payload = payload },
headers: metadata,
deadline: deadline,
cancellationToken: cancellationToken);
return response.Ok
? AltchaVerifyResult.Success
: AltchaVerifyResult.Fail(string.IsNullOrEmpty(response.Reason) ? "invalid" : response.Reason);
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
{
_logger.LogWarning(ex, "Altcha verify timed out against {Endpoint}.", opts.Endpoint);
return AltchaVerifyResult.Fail("verify-timeout");
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.Unavailable)
{
_logger.LogWarning(ex, "Altcha service unavailable at {Endpoint}.", opts.Endpoint);
return AltchaVerifyResult.Fail("service-unavailable");
}
catch (RpcException ex)
{
_logger.LogWarning(ex, "Altcha verify failed against {Endpoint}: {Status}", opts.Endpoint, ex.StatusCode);
return AltchaVerifyResult.Fail("rpc-error");
}
}
}