Add TfaRegistrationController for receiver TFA endpoints
Introduced TfaRegistrationController with endpoints to register and manage two-factor authentication for envelope receivers. Includes a GET endpoint to generate TFA registration metadata (QR code and deadline) and a POST endpoint to log out receivers. Implements error handling, logging, and uses dependency injection for required services. Added necessary using directives.
This commit is contained in:
@@ -0,0 +1,130 @@
|
|||||||
|
using DigitalData.Core.Abstraction.Application.DTO;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
|
using EnvelopeGenerator.Application.Resources;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using EnvelopeGenerator.GeneratorAPI.Extensions;
|
||||||
|
using EnvelopeGenerator.GeneratorAPI.Models;
|
||||||
|
using Ganss.Xss;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.GeneratorAPI.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes endpoints for registering and managing two-factor authentication for envelope receivers.
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/tfa")]
|
||||||
|
public class TfaRegistrationController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ILogger<TfaRegistrationController> _logger;
|
||||||
|
private readonly IEnvelopeReceiverService _envelopeReceiverService;
|
||||||
|
private readonly IAuthenticator _authenticator;
|
||||||
|
private readonly IReceiverService _receiverService;
|
||||||
|
private readonly TFARegParams _parameters;
|
||||||
|
private readonly IStringLocalizer<Resource> _localizer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TfaRegistrationController"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public TfaRegistrationController(
|
||||||
|
ILogger<TfaRegistrationController> logger,
|
||||||
|
IEnvelopeReceiverService envelopeReceiverService,
|
||||||
|
IAuthenticator authenticator,
|
||||||
|
IReceiverService receiverService,
|
||||||
|
IOptions<TFARegParams> tfaRegParamsOptions,
|
||||||
|
IStringLocalizer<Resource> localizer)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_envelopeReceiverService = envelopeReceiverService;
|
||||||
|
_authenticator = authenticator;
|
||||||
|
_receiverService = receiverService;
|
||||||
|
_parameters = tfaRegParamsOptions.Value;
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates registration metadata (QR code and deadline) for a receiver.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="envelopeReceiverId">Encoded envelope receiver id.</param>
|
||||||
|
[Authorize]
|
||||||
|
[HttpGet("{envelopeReceiverId}")]
|
||||||
|
public async Task<IActionResult> RegisterAsync(string envelopeReceiverId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var (uuid, signature) = envelopeReceiverId.DecodeEnvelopeReceiverId();
|
||||||
|
|
||||||
|
if (uuid is null || signature is null)
|
||||||
|
{
|
||||||
|
_logger.LogEnvelopeError(uuid: uuid, signature: signature, message: _localizer.WrongEnvelopeReceiverId());
|
||||||
|
return Unauthorized(new { message = _localizer.WrongEnvelopeReceiverId() });
|
||||||
|
}
|
||||||
|
|
||||||
|
var secretResult = await _envelopeReceiverService.ReadWithSecretByUuidSignatureAsync(uuid: uuid, signature: signature);
|
||||||
|
if (secretResult.IsFailed)
|
||||||
|
{
|
||||||
|
_logger.LogNotice(secretResult.Notices);
|
||||||
|
return NotFound(new { message = _localizer.WrongEnvelopeReceiverId() });
|
||||||
|
}
|
||||||
|
|
||||||
|
var envelopeReceiver = secretResult.Data;
|
||||||
|
|
||||||
|
if (!envelopeReceiver.Envelope!.TFAEnabled)
|
||||||
|
return Unauthorized(new { message = _localizer.WrongAccessCode() });
|
||||||
|
|
||||||
|
var receiver = envelopeReceiver.Receiver;
|
||||||
|
receiver!.TotpSecretkey = _authenticator.GenerateTotpSecretKey();
|
||||||
|
await _receiverService.UpdateAsync(receiver);
|
||||||
|
var totpQr64 = _authenticator.GenerateTotpQrCode(userEmail: receiver.EmailAddress, secretKey: receiver.TotpSecretkey).ToBase64String();
|
||||||
|
|
||||||
|
if (receiver.TfaRegDeadline is null)
|
||||||
|
{
|
||||||
|
receiver.TfaRegDeadline = _parameters.Deadline;
|
||||||
|
await _receiverService.UpdateAsync(receiver);
|
||||||
|
}
|
||||||
|
else if (receiver.TfaRegDeadline <= DateTime.Now)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status410Gone, new { message = _localizer.WrongAccessCode() });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
envelopeReceiver.EnvelopeId,
|
||||||
|
envelopeReceiver.Envelope!.Uuid,
|
||||||
|
envelopeReceiver.Receiver!.Signature,
|
||||||
|
receiver.TfaRegDeadline,
|
||||||
|
TotpQR64 = totpQr64
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogEnvelopeError(envelopeReceiverId: envelopeReceiverId, exception: ex, message: _localizer.WrongEnvelopeReceiverId());
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, new { message = _localizer.UnexpectedError() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs out the envelope receiver from cookie authentication.
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(Roles = ReceiverRole.FullyAuth)]
|
||||||
|
[HttpPost("auth/logout")]
|
||||||
|
public async Task<IActionResult> LogOutAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "{message}", ex.Message);
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, new { message = _localizer.UnexpectedError() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user