Add EnvelopeAuthService for SSR authentication
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.
This commit is contained in:
@@ -319,6 +319,9 @@ try
|
||||
builder.Services.AddScoped<SignatureCacheService>();
|
||||
builder.Services.AddSingleton<AppVersionService>();
|
||||
|
||||
// SSR Authentication Service (for Envelope Receiver pages)
|
||||
builder.Services.AddScoped<EnvelopeGenerator.Server.Services.IEnvelopeAuthService, EnvelopeGenerator.Server.Services.EnvelopeAuthService>();
|
||||
|
||||
// DevExpress Server-Side Services (CRITICAL for DxPdfViewer)
|
||||
builder.Services.AddDevExpressBlazor();
|
||||
builder.Services.AddDevExpressServerSideBlazorPdfViewer();
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace EnvelopeGenerator.Server.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service for handling envelope-specific authentication in SSR (Server-Side Rendering) context.
|
||||
/// </summary>
|
||||
public interface IEnvelopeAuthService
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the current user is authenticated for the given envelope key.
|
||||
/// Validates both that the user is authenticated AND that the envelope key matches their claims.
|
||||
/// </summary>
|
||||
/// <param name="envelopeKey">The envelope key to validate against user claims.</param>
|
||||
/// <returns>True if user is authenticated and envelope key matches; otherwise false.</returns>
|
||||
bool IsAuthenticated(string envelopeKey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authenticated envelope key from the current user's claims (NameIdentifier or "sub" claim).
|
||||
/// </summary>
|
||||
/// <returns>The envelope key if user is authenticated; otherwise null.</returns>
|
||||
string? GetAuthenticatedEnvelopeKey();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current HttpContext user principal.
|
||||
/// </summary>
|
||||
/// <returns>ClaimsPrincipal if available; otherwise null.</returns>
|
||||
ClaimsPrincipal? GetCurrentUser();
|
||||
}
|
||||
Reference in New Issue
Block a user