Introduced `EnvelopeAuthService` and `IEnvelopeAuthService` to handle server-side authentication for envelope receiver pages. - Registered `IEnvelopeAuthService` as a scoped service in `Program.cs`. - Implemented `EnvelopeAuthService` to validate user authentication and envelope key matching using `IHttpContextAccessor` and JWT claims. - Added methods to retrieve the authenticated envelope key and current user (`ClaimsPrincipal`). - Prioritized `NameIdentifier` claim for envelope key extraction, with fallback to `sub` claim. - Documented the service and interface with XML comments for clarity. This centralizes authentication logic, ensuring reusability and adherence to SSR best practices.
92 lines
2.6 KiB
C#
92 lines
2.6 KiB
C#
using System.Security.Claims;
|
|
|
|
namespace EnvelopeGenerator.Server.Services;
|
|
|
|
/// <summary>
|
|
/// Server-side authentication service for envelope receiver access validation.
|
|
/// Uses HttpContext to check JWT claims and envelope key authorization.
|
|
/// </summary>
|
|
public class EnvelopeAuthService : IEnvelopeAuthService
|
|
{
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
private readonly ILogger<EnvelopeAuthService> _logger;
|
|
|
|
public EnvelopeAuthService(
|
|
IHttpContextAccessor httpContextAccessor,
|
|
ILogger<EnvelopeAuthService> logger)
|
|
{
|
|
_httpContextAccessor = httpContextAccessor;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool IsAuthenticated(string envelopeKey)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(envelopeKey))
|
|
{
|
|
_logger.LogWarning("IsAuthenticated called with null or empty envelope key");
|
|
return false;
|
|
}
|
|
|
|
var context = _httpContextAccessor.HttpContext;
|
|
|
|
// Check if user is authenticated
|
|
if (context?.User?.Identity?.IsAuthenticated != true)
|
|
{
|
|
_logger.LogDebug("User is not authenticated for envelope {EnvelopeKey}", envelopeKey);
|
|
return false;
|
|
}
|
|
|
|
// Get envelope key from claims
|
|
var sub = GetEnvelopeKeyFromClaims(context.User);
|
|
|
|
// Verify envelope key matches
|
|
var isValid = sub == envelopeKey;
|
|
|
|
if (!isValid)
|
|
{
|
|
_logger.LogWarning(
|
|
"Envelope key mismatch: Expected {ExpectedKey}, Got {ActualKey}",
|
|
envelopeKey,
|
|
sub ?? "(null)");
|
|
}
|
|
else
|
|
{
|
|
_logger.LogDebug("User authenticated for envelope {EnvelopeKey}", envelopeKey);
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public string? GetAuthenticatedEnvelopeKey()
|
|
{
|
|
var context = _httpContextAccessor.HttpContext;
|
|
|
|
if (context?.User?.Identity?.IsAuthenticated != true)
|
|
return null;
|
|
|
|
return GetEnvelopeKeyFromClaims(context.User);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public ClaimsPrincipal? GetCurrentUser()
|
|
{
|
|
return _httpContextAccessor.HttpContext?.User;
|
|
}
|
|
|
|
private string? GetEnvelopeKeyFromClaims(ClaimsPrincipal user)
|
|
{
|
|
// Try NameIdentifier first (standard claim)
|
|
var sub = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
|
|
|
// Fallback to "sub" claim (JWT standard)
|
|
if (string.IsNullOrWhiteSpace(sub))
|
|
{
|
|
sub = user.FindFirst("sub")?.Value;
|
|
}
|
|
|
|
return sub;
|
|
}
|
|
}
|