diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln
index 4b13ac0a..b672495a 100644
--- a/EnvelopeGenerator.sln
+++ b/EnvelopeGenerator.sln
@@ -23,7 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{134D4164-B29
ProjectSection(SolutionItems) = preProject
COPILOT_CONTEXT.md = COPILOT_CONTEXT.md
FORM_APPLICATION_CONTEXT.md = FORM_APPLICATION_CONTEXT.md
- OPEN_SSR_TASK.md = OPEN_SSR_TASK.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0CBC2432-A561-4440-89BC-671B66A24146}"
diff --git a/OPEN_SSR_TASK.md b/OPEN_SSR_TASK.md
deleted file mode 100644
index 78358939..00000000
--- a/OPEN_SSR_TASK.md
+++ /dev/null
@@ -1,553 +0,0 @@
-# SSR Authentication Migration — Implementation Notes
-
-## Overview
-Migration from WASM client-side authentication to SSR (Server-Side Rendering) authentication for `EnvelopeReceiverPage.razor` to fix authentication issues in Blazor InteractiveServer mode.
-
----
-
-## Problem Statement
-
-### Issue
-`EnvelopeReceiverPage.razor` uses `@rendermode InteractiveServer` but was calling **WASM client service** `AuthService.CheckEnvelopeAccessAsync()`:
-
-```razor
-@inject EnvelopeGenerator.Server.Client.Services.AuthService AuthService
-
-var hasAccess = await AuthService.CheckEnvelopeAccessAsync(EnvelopeKey);
-```
-
-**Why This Failed:**
-- `AuthService` is a **WASM client service** that uses `IHttpClientFactory`
-- In SSR context, `HttpContext` is required to configure the base address
-- `CheckEnvelopeAccessAsync()` makes an HTTP request to `/api/auth/check/envelope/{key}`
-- This request **goes to itself** (server calling its own endpoint), causing issues
-- Returns `false` even when user is authenticated
-
----
-
-## Solution Architecture
-
-### Created New SSR Authentication Service
-
-**Files Created:**
-1. `EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs` (Interface)
-2. `EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs` (Implementation)
-
-**Purpose:** Direct `HttpContext.User` validation without HTTP requests
-
----
-
-## Implementation Details
-
-### 1. IEnvelopeAuthService Interface
-
-**Location:** `EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs`
-
-```csharp
-namespace EnvelopeGenerator.Server.Services;
-
-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.
- ///
- bool IsAuthenticated(string envelopeKey);
-
- ///
- /// Gets the authenticated envelope key from the current user's claims (NameIdentifier or "sub" claim).
- ///
- string? GetAuthenticatedEnvelopeKey();
-
- ///
- /// Gets the current HttpContext user principal.
- ///
- ClaimsPrincipal? GetCurrentUser();
-}
-```
-
-**Key Methods:**
-- `IsAuthenticated(string envelopeKey)`: Validates user auth + envelope key match
-- `GetAuthenticatedEnvelopeKey()`: Extracts envelope key from claims
-- `GetCurrentUser()`: Returns `ClaimsPrincipal` for advanced scenarios
-
----
-
-### 2. EnvelopeAuthService Implementation
-
-**Location:** `EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs`
-
-**Dependencies:**
-- `IHttpContextAccessor`: Access current HTTP context
-- `ILogger`: Structured logging
-
-**Logic:**
-```csharp
-public bool IsAuthenticated(string envelopeKey)
-{
- // 1. Validate envelope key parameter
- if (string.IsNullOrWhiteSpace(envelopeKey))
- return false;
-
- // 2. Get HttpContext
- var context = _httpContextAccessor.HttpContext;
-
- // 3. Check if user is authenticated
- if (context?.User?.Identity?.IsAuthenticated != true)
- return false;
-
- // 4. Extract envelope key from claims
- var sub = GetEnvelopeKeyFromClaims(context.User);
-
- // 5. Verify match
- return sub == envelopeKey;
-}
-
-private string? GetEnvelopeKeyFromClaims(ClaimsPrincipal user)
-{
- // Try standard claim first
- var sub = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
-
- // Fallback to JWT "sub" claim
- if (string.IsNullOrWhiteSpace(sub))
- sub = user.FindFirst("sub")?.Value;
-
- return sub;
-}
-```
-
-**Claim Priority:**
-1. `ClaimTypes.NameIdentifier` (standard .NET claim)
-2. `"sub"` (JWT standard claim)
-
----
-
-### 3. Service Registration
-
-**Location:** `EnvelopeGenerator.Server/Program.cs`
-
-**Added:**
-```csharp
-// SSR Authentication Service (for Envelope Receiver pages)
-builder.Services.AddScoped();
-```
-
-**Lifetime:** `Scoped` (per-request, matches `IHttpContextAccessor`)
-
----
-
-### 4. EnvelopeReceiverPage.razor Changes
-
-**Changes Made (REVERTED - To Be Re-Applied):**
-
-#### 4.1 Using Statements
-```razor
-@using EnvelopeGenerator.Server.Services
-```
-
-#### 4.2 Dependency Injection
-**Old:**
-```razor
-@inject EnvelopeGenerator.Server.Client.Services.AuthService AuthService
-```
-
-**New:**
-```razor
-@inject IEnvelopeAuthService EnvelopeAuth
-@inject IHttpClientFactory HttpClientFactory
-```
-
-#### 4.3 Authentication Check in `OnInitializedAsync()`
-**Old:**
-```csharp
-var hasAccess = await AuthService.CheckEnvelopeAccessAsync(EnvelopeKey);
-if (!hasAccess) {
- Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}");
- return;
-}
-```
-
-**New:**
-```csharp
-// ? SSR Authentication check via service
-if (!EnvelopeAuth.IsAuthenticated(EnvelopeKey)) {
- Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}");
- return;
-}
-```
-
-**Benefits:**
-- ? Synchronous (no HTTP overhead)
-- ? Direct `HttpContext.User` access
-- ? No self-referencing HTTP calls
-- ? Works in SSR context
-
-#### 4.4 Logout Method
-**Old:**
-```csharp
-await AuthService.LogoutEnvelopeReceiverAsync(EnvelopeKey);
-```
-
-**New:**
-```csharp
-try
-{
- // ? SSR: Direct HTTP call instead of WASM client service
- using var http = HttpClientFactory.CreateClient("EnvelopeGenerator.Server");
- await http.PostAsync($"/api/auth/logout/envelope/{Uri.EscapeDataString(EnvelopeKey)}", null);
-}
-catch (Exception ex)
-{
- logger.LogError(ex, "Logout failed for envelope {EnvelopeKey}", EnvelopeKey);
-}
-
-Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}", forceLoad: true);
-```
-
-**Why Changed:**
-- WASM `AuthService.LogoutEnvelopeReceiverAsync()` doesn't work in SSR
-- Use named HttpClient `"EnvelopeGenerator.Server"` (configured in `Program.cs`)
-- Graceful error handling (logout errors shouldn't block redirect)
-
----
-
-## Remaining Tasks
-
-### ? Completed
-1. ? Created `IEnvelopeAuthService` interface
-2. ? Implemented `EnvelopeAuthService` with `HttpContext` access
-3. ? Registered service in `Program.cs`
-4. ?? **REVERTED** `EnvelopeReceiverPage.razor` changes (merge conflict)
-
-### ? TODO (Next Agent)
-
-#### 1. Re-apply EnvelopeReceiverPage.razor Changes
-**File:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor`
-
-**Steps:**
-1. Add using statement:
-```razor
-@using EnvelopeGenerator.Server.Services
-```
-
-2. Replace injection:
-```razor
-@inject IEnvelopeAuthService EnvelopeAuth
-@inject IHttpClientFactory HttpClientFactory
-```
-
- Remove:
-```razor
-@inject EnvelopeGenerator.Server.Client.Services.AuthService AuthService
-```
-
-3. Update `OnInitializedAsync()` authentication check:
-```csharp
-// Replace this:
-var hasAccess = await AuthService.CheckEnvelopeAccessAsync(EnvelopeKey);
-if (!hasAccess) {
- Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}");
- return;
-}
-
-// With this:
-if (!EnvelopeAuth.IsAuthenticated(EnvelopeKey)) {
- Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}");
- return;
-}
-```
-
-4. Update `LogoutAsync()` method:
-```csharp
-async Task LogoutAsync() {
- if (string.IsNullOrWhiteSpace(EnvelopeKey) || _isLoggingOut) return;
- _isLoggingOut = true;
- await InvokeAsync(StateHasChanged);
-
- try
- {
- // ? SSR: Direct HTTP call instead of WASM client service
- using var http = HttpClientFactory.CreateClient("EnvelopeGenerator.Server");
- await http.PostAsync($"/api/auth/logout/envelope/{Uri.EscapeDataString(EnvelopeKey)}", null);
- }
- catch (Exception ex)
- {
- logger.LogError(ex, "Logout failed for envelope {EnvelopeKey}", EnvelopeKey);
- }
-
- Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}", forceLoad: true);
-}
-```
-
-#### 2. Test Authentication Flow
-**Scenarios:**
-- ? Valid cookie ? Page loads
-- ? Invalid cookie ? Redirect to login
-- ? No cookie ? Redirect to login
-- ? Envelope key mismatch ? Redirect to login
-- ? Logout ? Cookie cleared, redirect to login
-
-#### 3. Remove WASM Client Services from SSR Pages
-**Optional Cleanup:**
-- Review other SSR pages (`EnvelopeReceiverPage_DxPdfViewer.razor`, etc.)
-- Replace WASM client services with SSR equivalents where applicable
-- Document which services are WASM-only vs SSR-compatible
-
----
-
-## Authentication Flow Comparison
-
-### ? Old Flow (WASM Client Service in SSR)
-```
-EnvelopeReceiverPage (@rendermode InteractiveServer)
- ?
-AuthService.CheckEnvelopeAccessAsync() (WASM client)
- ?
-IHttpClientFactory.CreateClient("EnvelopeGenerator.Server")
- ?
-GET /api/auth/check/envelope/{key}
- ?
-[SELF-REFERENCING REQUEST - FAILS]
- ?
-Returns false even when authenticated
-```
-
-### ? New Flow (SSR Service)
-```
-EnvelopeReceiverPage (@rendermode InteractiveServer)
- ?
-IEnvelopeAuthService.IsAuthenticated(envelopeKey)
- ?
-IHttpContextAccessor.HttpContext.User (Direct access)
- ?
-ClaimsPrincipal.FindFirst("sub" or NameIdentifier)
- ?
-Compare with envelopeKey
- ?
-Return true/false (synchronous, no HTTP)
-```
-
----
-
-## Technical Decisions
-
-### Why Not Use `[Authorize]` Attribute?
-- Blazor SSR components **don't support** `[Authorize]` at component level
-- Would require `` component (less clean)
-- Custom service provides more control + logging
-
-### Why Scoped Lifetime?
-- `IHttpContextAccessor` is scoped (per-request)
-- `EnvelopeAuthService` depends on `IHttpContextAccessor`
-- Scoped ensures same `HttpContext` throughout request
-
-### Why Two Claims (`NameIdentifier` + `"sub"`)?
-- **`NameIdentifier`**: Standard .NET claim type
-- **`"sub"`**: JWT standard claim
-- Fallback ensures compatibility with different token formats
-
----
-
-## Logging & Debugging
-
-### Log Levels
-- **Debug:** Successful authentication
-- **Warning:** Null envelope key, key mismatch
-- **Error:** (Reserved for future exceptions)
-
-### Sample Logs
-```
-[Debug] User authenticated for envelope 517bb9c5-6082-4e61-aaa5-9846386e67ee
-[Warning] Envelope key mismatch: Expected abc123, Got 517bb9c5-6082-4e61-aaa5-9846386e67ee
-[Warning] IsAuthenticated called with null or empty envelope key
-```
-
----
-
-## Testing Checklist
-
-### Unit Tests (TODO)
-```csharp
-// EnvelopeGenerator.Tests/Services/EnvelopeAuthServiceTests.cs
-[Fact]
-public void IsAuthenticated_ValidUser_ReturnsTrue() { ... }
-
-[Fact]
-public void IsAuthenticated_InvalidKey_ReturnsFalse() { ... }
-
-[Fact]
-public void IsAuthenticated_UnauthenticatedUser_ReturnsFalse() { ... }
-
-[Fact]
-public void GetAuthenticatedEnvelopeKey_ValidUser_ReturnsKey() { ... }
-```
-
-### Integration Tests (Manual)
-1. ? Login with valid access code ? Cookie set
-2. ? Navigate to `/envelope/{key}` ? Page loads
-3. ? Logout ? Cookie cleared, redirect
-4. ? Try accessing `/envelope/{key}` without cookie ? Redirect to login
-5. ? Try accessing `/envelope/{wrongKey}` with valid cookie ? Redirect to login
-
----
-
-## Migration Checklist
-
-- [x] Create `IEnvelopeAuthService` interface
-- [x] Implement `EnvelopeAuthService`
-- [x] Register service in `Program.cs`
-- [ ] **Re-apply** `EnvelopeReceiverPage.razor` changes (after merge)
-- [ ] Test authentication flow
-- [ ] Add unit tests
-- [ ] Update other SSR pages (if needed)
-- [ ] Document in `COPILOT_CONTEXT.md`
-
----
-
-## Documentation Updates Needed
-
-### COPILOT_CONTEXT.md
-
-**Add Section:**
-```markdown
-## SSR Authentication Service
-
-**Purpose:** Server-side authentication for Blazor InteractiveServer pages.
-
-**Location:** `EnvelopeGenerator.Server/Services/`
-
-**Service:** `IEnvelopeAuthService` / `EnvelopeAuthService`
-
-**Usage:**
-```razor
-@inject IEnvelopeAuthService EnvelopeAuth
-
-protected override async Task OnInitializedAsync() {
- if (!EnvelopeAuth.IsAuthenticated(EnvelopeKey)) {
- Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}");
- return;
- }
-}
-```
-
-**Why Not Use WASM Client Services in SSR?**
-- WASM client services use `IHttpClientFactory` with base address configuration
-- SSR context requires `HttpContext` to configure base address
-- Calling API endpoints from server-side component creates self-referencing requests
-- Use `IEnvelopeAuthService` for direct `HttpContext.User` access instead
-
-**Authentication Flow:**
-1. JWT token stored in per-envelope cookie (`AuthTokenSignFLOWReceiver.{envelopeKey}`)
-2. JWT middleware validates token, sets `HttpContext.User`
-3. `EnvelopeAuthService` checks `ClaimsPrincipal.FindFirst("sub")` or `NameIdentifier`
-4. Compares claim value with route parameter `{EnvelopeKey}`
-
-**Claim Priority:**
-1. `ClaimTypes.NameIdentifier` (standard .NET)
-2. `"sub"` (JWT standard)
-
-**Service Lifetime:** Scoped (per-request)
-```
-
----
-
-## Common Mistakes to Avoid
-
-### ? Don't Do This
-```csharp
-// SSR page using WASM client service
-@inject EnvelopeGenerator.Server.Client.Services.AuthService AuthService
-
-var hasAccess = await AuthService.CheckEnvelopeAccessAsync(EnvelopeKey);
-```
-
-**Why Wrong:**
-- Creates self-referencing HTTP request
-- WASM client service doesn't work in SSR context
-- Always returns `false` even when authenticated
-
-### ? Do This Instead
-```csharp
-// SSR page using SSR authentication service
-@inject IEnvelopeAuthService EnvelopeAuth
-
-if (!EnvelopeAuth.IsAuthenticated(EnvelopeKey)) {
- Navigation.NavigateTo($"/envelope/login/{Uri.EscapeDataString(EnvelopeKey)}");
- return;
-}
-```
-
-**Why Correct:**
-- Direct `HttpContext.User` access
-- Synchronous (no HTTP overhead)
-- Works in SSR context
-
----
-
-## References
-
-### Related Files
-- `EnvelopeGenerator.Server/Program.cs` (Service registration, JWT middleware)
-- `EnvelopeGenerator.Server.Client/Services/AuthService.cs` (WASM client version)
-- `EnvelopeGenerator.Server.Client/Pages/LoginReceiverPage.razor` (WASM login page)
-- `EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` (SSR viewer page)
-
-### JWT Configuration
-**File:** `EnvelopeGenerator.Server/Program.cs`
-
-```csharp
-.AddJwtBearer(AuthScheme.Receiver, opt =>
-{
- opt.Events = new JwtBearerEvents
- {
- OnMessageReceived = context =>
- {
- var envelopeKey = context.Request.Path.Value?.Split('/').LastOrDefault();
- if (envelopeKey is not null)
- {
- var cookieName = CookieNames.GetEnvelopeReceiverCookieName(authTokenKeys.Cookie, envelopeKey);
- if (context.Request.Cookies.TryGetValue(cookieName, out var cookieToken))
- context.Token = cookieToken;
- }
- return Task.CompletedTask;
- },
- OnTokenValidated = context =>
- {
- var envelopeKey = context.Request.Path.Value?.Split('/').LastOrDefault();
- var sub = context.Principal?.FindFirst("sub")?.Value;
-
- if (envelopeKey is null || sub != envelopeKey)
- context.Fail("Envelope key mismatch");
-
- return Task.CompletedTask;
- }
- };
-});
-```
-
----
-
-## Summary
-
-**What Was Done:**
-1. Created SSR authentication service (`IEnvelopeAuthService` / `EnvelopeAuthService`)
-2. Registered service in DI container
-3. Updated `EnvelopeReceiverPage.razor` (REVERTED due to merge)
-
-**What's Left:**
-1. **Re-apply** `EnvelopeReceiverPage.razor` changes after merge
-2. Test authentication flow
-3. Add unit tests
-4. Update documentation
-
-**Key Insight:**
-- **WASM client services ? SSR server services**
-- Use `IHttpContextAccessor` for direct `HttpContext.User` access in SSR
-- Avoid HTTP requests from server-side components to own endpoints
-
----
-
-**Last Updated:** 2025-01-27
-**Status:** ?? Partial (Service created, page changes reverted for merge)
-**Next Agent:** Re-apply `EnvelopeReceiverPage.razor` changes + testing