67 lines
2.4 KiB
C#
67 lines
2.4 KiB
C#
using Api.Services.Contracts.Models;
|
|
using Api.Services.Contracts;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Options;
|
|
using Api.Models.Settings;
|
|
using Swashbuckle.AspNetCore.Annotations;
|
|
|
|
namespace Api.Controllers
|
|
{
|
|
/// <summary>
|
|
/// Endpoints that expose captcha configuration and verification.
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public sealed class CaptchaController : ControllerBase
|
|
{
|
|
private readonly CaptchaSettings _captchaSettings;
|
|
private readonly ICaptchaVerifier _captcha;
|
|
private readonly ILogger<CaptchaController> _log;
|
|
|
|
public CaptchaController(IOptions<CaptchaSettings> options, ICaptchaVerifier captcha, ILogger<CaptchaController> log)
|
|
{
|
|
_captchaSettings = options.Value;
|
|
_captcha = captcha;
|
|
_log = log;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the public reCAPTCHA site key used by the client to render the widget.
|
|
/// </summary>
|
|
[HttpGet]
|
|
[SwaggerOperation(Summary = "Get reCAPTCHA site key")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public IActionResult GetSiteKey()
|
|
{
|
|
return Ok(_captchaSettings.PublicKey);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verify a captcha token and return the verification verdict.
|
|
/// </summary>
|
|
[HttpPost("verify")]
|
|
[SwaggerOperation(Summary = "Verify captcha token")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
public async Task<IActionResult> Verify([FromBody] VerifyRequest req, CancellationToken ct)
|
|
{
|
|
if (req is null || string.IsNullOrWhiteSpace(req.Token)) return BadRequest(new { error = "Missing token" });
|
|
|
|
var userIp = HttpContext.Connection.RemoteIpAddress?.ToString();
|
|
var verdict = await _captcha.VerifyAsync(req.Token, userIp, ct);
|
|
if (!verdict.Success)
|
|
{
|
|
_log.LogWarning("Captcha failed. ip={Ip} score={Score} err={Err}", userIp, verdict.Score, verdict.Error);
|
|
return BadRequest(new { error = "Captcha verification failed.", score = verdict.Score });
|
|
}
|
|
|
|
return Ok(verdict);
|
|
}
|
|
|
|
public sealed class VerifyRequest
|
|
{
|
|
public string? Token { get; set; }
|
|
}
|
|
}
|
|
}
|