From ae5690275812e14c2264d59199461beca70fc8ef Mon Sep 17 00:00:00 2001 From: TekH Date: Thu, 2 Jul 2026 01:48:25 +0200 Subject: [PATCH] Add SenderAuthCookieHandler for cookie-based JWT auth Added a custom DelegatingHandler, SenderAuthCookieHandler, to forward the browser's Cookie header to outgoing HttpClient requests in Blazor Server. Registered the handler as a transient service and integrated it into the named HttpClient pipeline for internal API calls. This enables Blazor Server components to make authenticated API calls using cookie-based JWT authentication (AuthScheme.Sender). --- .../Handlers/SenderAuthCookieHandler.cs | 33 +++++++++++++++++++ .../EnvelopeGenerator.Server/Program.cs | 6 +++- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 EnvelopeGenerator.Server/EnvelopeGenerator.Server/Handlers/SenderAuthCookieHandler.cs diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Handlers/SenderAuthCookieHandler.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Handlers/SenderAuthCookieHandler.cs new file mode 100644 index 00000000..27ea8c08 --- /dev/null +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Handlers/SenderAuthCookieHandler.cs @@ -0,0 +1,33 @@ +namespace EnvelopeGenerator.Server.Handlers; + +/// +/// A that forwards the incoming HTTP request's +/// Cookie header to all outgoing calls +/// made by Blazor Server components. +/// +/// Problem it solves: +/// Blazor Server runs on the server process. When a component calls an API endpoint +/// that requires cookie-based JWT authentication (AuthScheme.Sender), the HttpClient +/// does not automatically include the browser's cookies — those only travel with +/// browser-initiated requests. This handler copies the Cookie header from the +/// current into every outgoing request +/// so that the API's JwtBearer OnMessageReceived callback can extract the token. +/// +/// Thread safety: +/// The handler is registered as Transient and is resolved per-request by the +/// IHttpClientFactory pipeline, so there is no shared state between requests. +/// +public class SenderAuthCookieHandler(IHttpContextAccessor httpContextAccessor) : DelegatingHandler +{ + protected override Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) + { + var cookieHeader = httpContextAccessor.HttpContext?.Request.Headers["Cookie"].ToString(); + + if (!string.IsNullOrWhiteSpace(cookieHeader)) + request.Headers.TryAddWithoutValidation("Cookie", cookieHeader); + + return base.SendAsync(request, cancellationToken); + } +} diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs index fbdb824c..0a0769c6 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs @@ -69,6 +69,9 @@ try builder.Services.AddHttpContextAccessor(); // Named HttpClient for internal API calls + // SenderAuthCookieHandler forwards the browser's Cookie header so that + // Blazor Server components can call cookie-authenticated endpoints (AuthScheme.Sender). + builder.Services.AddTransient(); builder.Services.AddHttpClient("EnvelopeGenerator.Server", (sp, client) => { var httpContextAccessor = sp.GetRequiredService(); @@ -79,7 +82,8 @@ try // Set base address to current host for SSR scenarios client.BaseAddress = new Uri($"{request.Scheme}://{request.Host}"); } - }); + }) + .AddHttpMessageHandler(); // CORS Policy var allowedOrigins = config.GetSection("AllowedOrigins").Get() ??