Refactored `EnvelopeReceiverPage.razor` to use new services for receiver authentication and data retrieval. Introduced `EnvelopeReceiverAuthorizationService` for handling JWT-based authorization and `EnvelopeReceiverPageDataService` for centralized data access and caching. Updated dependency injection in `Program.cs` to register these services. Replaced direct service calls with `PageDataService` methods for document, signature, and receiver data retrieval. Improved logging with `ILogger` and added debug logs for token validation. Enhanced modularity, maintainability, and performance by consolidating logic and reducing coupling between components.
94 lines
3.6 KiB
C#
94 lines
3.6 KiB
C#
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using DigitalData.Auth.Claims;
|
|
using EnvelopeGenerator.Domain.Constants;
|
|
using EnvelopeGenerator.Server.Models;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace EnvelopeGenerator.Server.Services;
|
|
|
|
/// <summary>
|
|
/// Authorizes receiver access for interactive server pages without calling a controller endpoint.
|
|
/// </summary>
|
|
public class EnvelopeReceiverAuthorizationService(
|
|
IHttpContextAccessor httpContextAccessor,
|
|
IAuthorizationService authorizationService,
|
|
IOptions<AuthTokenKeys> authTokenKeyOptions,
|
|
IOptionsMonitor<JwtBearerOptions> jwtBearerOptionsMonitor,
|
|
ILogger<EnvelopeReceiverAuthorizationService> logger)
|
|
{
|
|
private readonly AuthTokenKeys _authTokenKeys = authTokenKeyOptions.Value;
|
|
|
|
/// <summary>
|
|
/// Returns the authenticated receiver principal for the specified envelope key when authorization succeeds.
|
|
/// </summary>
|
|
public async Task<ClaimsPrincipal?> AuthorizeAsync(string envelopeKey, CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(envelopeKey))
|
|
return null;
|
|
|
|
var httpContext = httpContextAccessor.HttpContext;
|
|
if (httpContext is null)
|
|
return null;
|
|
|
|
if (await IsAuthorizedReceiverAsync(httpContext.User, envelopeKey, cancellationToken))
|
|
return httpContext.User;
|
|
|
|
var cookieName = CookieNames.GetEnvelopeReceiverCookieName(_authTokenKeys.Cookie, envelopeKey);
|
|
if (!httpContext.Request.Cookies.TryGetValue(cookieName, out var token) || string.IsNullOrWhiteSpace(token))
|
|
{
|
|
logger.LogDebug("Receiver cookie '{CookieName}' was not found for envelope '{EnvelopeKey}'.", cookieName, envelopeKey);
|
|
return null;
|
|
}
|
|
|
|
var principal = ValidateReceiverToken(token);
|
|
if (principal is null)
|
|
return null;
|
|
|
|
if (!await IsAuthorizedReceiverAsync(principal, envelopeKey, cancellationToken))
|
|
return null;
|
|
|
|
httpContext.User = principal;
|
|
|
|
return principal;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks whether the current request is authorized for the specified envelope key.
|
|
/// </summary>
|
|
public async Task<bool> IsAuthorizedAsync(string envelopeKey, CancellationToken cancellationToken = default)
|
|
=> await AuthorizeAsync(envelopeKey, cancellationToken) is not null;
|
|
|
|
private async Task<bool> IsAuthorizedReceiverAsync(ClaimsPrincipal? principal, string envelopeKey, CancellationToken cancellationToken)
|
|
{
|
|
if (principal?.Identity?.IsAuthenticated != true)
|
|
return false;
|
|
|
|
var authorizationResult = await authorizationService.AuthorizeAsync(principal, AuthPolicy.Receiver);
|
|
if (!authorizationResult.Succeeded)
|
|
return false;
|
|
|
|
var subject = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value
|
|
?? principal.FindFirst("sub")?.Value;
|
|
|
|
return string.Equals(subject, envelopeKey, StringComparison.Ordinal);
|
|
}
|
|
|
|
private ClaimsPrincipal? ValidateReceiverToken(string token)
|
|
{
|
|
try
|
|
{
|
|
var tokenValidationParameters = jwtBearerOptionsMonitor.Get(AuthScheme.Receiver).TokenValidationParameters.Clone();
|
|
var tokenHandler = new JwtSecurityTokenHandler();
|
|
return tokenHandler.ValidateToken(token, tokenValidationParameters, out _);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogDebug(ex, "Receiver token validation failed.");
|
|
return null;
|
|
}
|
|
}
|
|
}
|