Add envelope receiver access code request feature
Introduced a feature to allow envelope receivers to request an access code via email on the login page. - Added `OnInitializedAsync` in `LoginReceiverPage.razor` to trigger access code requests using `AuthService`. - Implemented `RequestEnvelopeReceiverAccessCodeAsync` in `AuthService.cs` to handle API calls for access code requests. - Added a new endpoint in `AuthController` to process access code requests, including validation, history recording, and email dispatching. - Registered `IEnvelopeMailService` and `AddDispatcher` in `Program.cs` to support email dispatch and database operations. - Refactored `AuthController` constructor for readability and added necessary `using` directives. - Improved logging and error handling for better traceability and robustness. These changes ensure a seamless and secure process for envelope receivers to request access codes, enhancing the user experience.
This commit is contained in:
@@ -144,6 +144,14 @@
|
||||
bool IsLoading;
|
||||
EnvelopeLoginResult? LoginResult;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(EnvelopeKey))
|
||||
return;
|
||||
|
||||
_ = await AuthService.RequestEnvelopeReceiverAccessCodeAsync(EnvelopeKey);
|
||||
}
|
||||
|
||||
async Task OnKeyDownAsync(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Key == "Enter")
|
||||
|
||||
@@ -33,6 +33,20 @@ public class AuthService(IHttpClientFactory httpClientFactory)
|
||||
return response.StatusCode == HttpStatusCode.OK;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers access code e-mail request flow for the receiver login page.
|
||||
/// Calls POST /api/Auth/envelope-receiver/{key}/request-access-code.
|
||||
/// </summary>
|
||||
public async Task<bool> RequestEnvelopeReceiverAccessCodeAsync(string envelopeKey, CancellationToken cancel = default)
|
||||
{
|
||||
using var http = CreateDefaultClient();
|
||||
var response = await http.PostAsync(
|
||||
$"/api/Auth/envelope-receiver/{Uri.EscapeDataString(envelopeKey)}/request-access-code",
|
||||
null,
|
||||
cancel);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits the access code for the given envelope key.
|
||||
/// Calls POST /api/Auth/envelope-receiver/{key} with multipart/form-data.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using DigitalData.Auth.Claims;
|
||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
||||
using EnvelopeGenerator.Server.Controllers.Interfaces;
|
||||
using EnvelopeGenerator.Server.Models;
|
||||
using EnvelopeGenerator.Domain.Constants;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -15,7 +18,9 @@ namespace EnvelopeGenerator.Server.Controllers;
|
||||
/// </summary>
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public partial class AuthController(IOptions<AuthTokenKeys> authTokenKeyOptions, IAuthorizationService authService) : ControllerBase, IAuthController
|
||||
public partial class AuthController(
|
||||
IOptions<AuthTokenKeys> authTokenKeyOptions,
|
||||
IAuthorizationService authService) : ControllerBase, IAuthController
|
||||
{
|
||||
private readonly AuthTokenKeys authTokenKeys = authTokenKeyOptions.Value;
|
||||
|
||||
@@ -82,6 +87,64 @@ public partial class AuthController(IOptions<AuthTokenKeys> authTokenKeyOptions,
|
||||
[HttpGet("check/envelope/{envelopeKey}")]
|
||||
public IActionResult CheckEnvelopeReceiver([FromRoute] string envelopeKey) => Ok();
|
||||
|
||||
/// <summary>
|
||||
/// Sends the receiver access code e-mail on the first envelope login-page visit.
|
||||
/// The operation is idempotent: if the code was already requested, it returns 200 without re-sending.
|
||||
/// </summary>
|
||||
/// <param name="key">Envelope receiver key.</param>
|
||||
/// <response code="200">Access code is already available or requested successfully.</response>
|
||||
/// <response code="404">Envelope receiver key was not found.</response>
|
||||
/// <response code="500">Access code could not be persisted or e-mail could not be sent.</response>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("envelope-receiver/{key}/request-access-code")]
|
||||
public async Task<IActionResult> RequestEnvelopeReceiverAccessCode(
|
||||
[FromRoute] string key,
|
||||
[FromServices] IMediator mediator,
|
||||
[FromServices] IEnvelopeHistoryService historyService,
|
||||
[FromServices] IEnvelopeMailService mailService,
|
||||
[FromServices] ILogger<AuthController> logger,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var envelopeReceiver = (await mediator.Send(new ReadEnvelopeReceiverQuery { Key = key }, cancel)).SingleOrDefault();
|
||||
|
||||
if (envelopeReceiver is null || envelopeReceiver.Envelope is null || envelopeReceiver.Receiver is null)
|
||||
return NotFound();
|
||||
|
||||
if (envelopeReceiver.Envelope.UseAccessCode != true)
|
||||
return Ok();
|
||||
|
||||
var userReference = envelopeReceiver.Receiver.EmailAddress;
|
||||
|
||||
if (await historyService.IsSigned(envelopeReceiver.EnvelopeId, userReference))
|
||||
return Ok();
|
||||
|
||||
var accessCodeAlreadyRequested = await historyService.AccessCodeAlreadyRequested(envelopeReceiver.EnvelopeId, userReference);
|
||||
if (accessCodeAlreadyRequested)
|
||||
return Ok();
|
||||
|
||||
var historyResult = await historyService.RecordAsync(envelopeReceiver.EnvelopeId, userReference, EnvelopeStatus.AccessCodeRequested);
|
||||
if (historyResult.IsFailed)
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Failed to record AccessCodeRequested history for envelope {EnvelopeId} and receiver {ReceiverEmail}.",
|
||||
envelopeReceiver.EnvelopeId,
|
||||
userReference);
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
|
||||
var mailResult = await mailService.SendAccessCodeAsync(envelopeReceiver);
|
||||
if (mailResult.IsFailed)
|
||||
{
|
||||
logger.LogWarning(
|
||||
"Failed to send access code mail for envelope {EnvelopeId} and receiver {ReceiverEmail}.",
|
||||
envelopeReceiver.EnvelopeId,
|
||||
userReference);
|
||||
return StatusCode(StatusCodes.Status500InternalServerError);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the per-envelope receiver cookie for the given envelope key.
|
||||
/// </summary>
|
||||
@@ -108,4 +171,4 @@ public partial class AuthController(IOptions<AuthTokenKeys> authTokenKeyOptions,
|
||||
Response.Cookies.Delete(cookieName);
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ using NLog.Web;
|
||||
using NLog;
|
||||
using DigitalData.Auth.Claims;
|
||||
using EnvelopeGenerator.Server;
|
||||
using DigitalData.EmailProfilerDispatcher;
|
||||
|
||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||
logger.Info("EnvelopeGenerator.Server logging initialized!");
|
||||
@@ -323,6 +324,8 @@ try
|
||||
.AddEnvelopeGeneratorServices(config);
|
||||
#pragma warning restore CS0618
|
||||
|
||||
builder.Services.AddDispatcher<EGDbContext>();
|
||||
|
||||
// User Preferences (theme, dark mode, grid layouts — stored in TBDD_CACHE)
|
||||
// Registration handled by Infrastructure DependencyInjection (AddEnvelopeGeneratorInfrastructureServices)
|
||||
|
||||
@@ -346,6 +349,7 @@ try
|
||||
builder.Services.AddScoped<EnvelopeReceiverService>();
|
||||
builder.Services.AddScoped<SignatureService>();
|
||||
builder.Services.AddScoped<SignatureCacheService>();
|
||||
builder.Services.AddScoped<EnvelopeGenerator.Application.Common.Interfaces.Services.IEnvelopeMailService, EnvelopeGenerator.Application.Services.EnvelopeMailService>();
|
||||
builder.Services.AddSingleton<AppVersionService>();
|
||||
|
||||
// EnvelopeService with HttpClient factory (for SSR scenarios)
|
||||
|
||||
Reference in New Issue
Block a user