From ed17852542be8d0a05d93c32d5b5da0c67e068a6 Mon Sep 17 00:00:00 2001 From: TekH Date: Wed, 24 Jun 2026 15:57:06 +0200 Subject: [PATCH] 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. --- .../EnvelopeGenerator.Server/Program.cs | 3 + .../Services/EnvelopeAuthService.cs | 91 +++++++++++++++++++ .../Services/IEnvelopeAuthService.cs | 29 ++++++ 3 files changed, 123 insertions(+) create mode 100644 EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs create mode 100644 EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs index 0ed711b3..ad1e8881 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs @@ -319,6 +319,9 @@ try builder.Services.AddScoped(); builder.Services.AddSingleton(); + // SSR Authentication Service (for Envelope Receiver pages) + builder.Services.AddScoped(); + // DevExpress Server-Side Services (CRITICAL for DxPdfViewer) builder.Services.AddDevExpressBlazor(); builder.Services.AddDevExpressServerSideBlazorPdfViewer(); diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs new file mode 100644 index 00000000..9c2dac20 --- /dev/null +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs @@ -0,0 +1,91 @@ +using System.Security.Claims; + +namespace EnvelopeGenerator.Server.Services; + +/// +/// Server-side authentication service for envelope receiver access validation. +/// Uses HttpContext to check JWT claims and envelope key authorization. +/// +public class EnvelopeAuthService : IEnvelopeAuthService +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public EnvelopeAuthService( + IHttpContextAccessor httpContextAccessor, + ILogger logger) + { + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } + + /// + 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; + } + + /// + public string? GetAuthenticatedEnvelopeKey() + { + var context = _httpContextAccessor.HttpContext; + + if (context?.User?.Identity?.IsAuthenticated != true) + return null; + + return GetEnvelopeKeyFromClaims(context.User); + } + + /// + 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; + } +} diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs new file mode 100644 index 00000000..cafd90ad --- /dev/null +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs @@ -0,0 +1,29 @@ +using System.Security.Claims; + +namespace EnvelopeGenerator.Server.Services; + +/// +/// Service for handling envelope-specific authentication in SSR (Server-Side Rendering) context. +/// +public interface IEnvelopeAuthService +{ + /// + /// 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. + /// + /// The envelope key to validate against user claims. + /// True if user is authenticated and envelope key matches; otherwise false. + bool IsAuthenticated(string envelopeKey); + + /// + /// Gets the authenticated envelope key from the current user's claims (NameIdentifier or "sub" claim). + /// + /// The envelope key if user is authenticated; otherwise null. + string? GetAuthenticatedEnvelopeKey(); + + /// + /// Gets the current HttpContext user principal. + /// + /// ClaimsPrincipal if available; otherwise null. + ClaimsPrincipal? GetCurrentUser(); +}