This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using Api.Services.Contracts;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Models.Settings;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
using Models.Requests;
|
||||
using Shared.Models.Responses;
|
||||
|
||||
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 captcha 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(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, "Captcha verification failed or token missing", typeof(ErrorResponse))]
|
||||
public async Task<IActionResult> Verify([FromBody] CaptchaVerifyRequest req, CancellationToken ct)
|
||||
{
|
||||
if (req is null || string.IsNullOrWhiteSpace(req.Token))
|
||||
{
|
||||
return BadRequest(new ErrorResponse { Error = "Missing token", Code = "captcha_token_missing" });
|
||||
}
|
||||
|
||||
var userIp = HttpContext.Connection.RemoteIpAddress?.ToString();
|
||||
var verdict = await _captcha.VerifyAsync(req.Token, userIp, req.ExpectedAction, ct);
|
||||
if (!verdict.Success)
|
||||
{
|
||||
_log.LogWarning("Captcha failed. ip={Ip} score={Score} err={Err}", userIp, verdict.Score, verdict.Error);
|
||||
return BadRequest(new ErrorResponse
|
||||
{
|
||||
Error = "Captcha verification failed.",
|
||||
Code = "captcha_verification_failed",
|
||||
Score = verdict.Score
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(verdict);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user