# Web API and Authentication - Migration Deltas ## Purpose This document specifies requirements ADDED or MODIFIED for the .NET 10 migration of the Web API and authentication layer. These requirements extend the base specification at `openspec/specs/web-api-auth/spec.md`. ## ADDED Requirements ### Requirement: Cross-Platform LDAP Support The system SHALL use `System.DirectoryServices.Protocols` for LDAP authentication to ensure cross-platform compatibility. #### Business Rules - The system SHALL NOT use `System.DirectoryServices.DirectoryEntry` or `System.DirectoryServices.DirectorySearcher` (Windows-only APIs) - The system SHALL use `LdapConnection` from `System.DirectoryServices.Protocols` - The system SHALL use asynchronous patterns with `Task.Run()` for LDAP operations (the LDAP API is synchronous) - The system SHALL dispose `LdapConnection` after each operation #### Scenario: LDAP authentication on Linux - **WHEN** the application runs on Linux with LDAP configuration - **THEN** the system authenticates successfully using `System.DirectoryServices.Protocols` ### Requirement: ClosedXML for Excel Operations The system SHALL use ClosedXML library for Excel file parsing in the File API. #### Business Rules - The system SHALL NOT use EPPlus (commercial license required in v7+) - The system SHALL use `XLWorkbook` from ClosedXML for reading uploaded Excel files - The system SHALL use 1-indexed row/column access matching ClosedXML conventions - The system SHALL handle empty worksheets gracefully using `LastRowUsed()?.RowNumber()` #### Scenario: Parse Excel upload with ClosedXML - **WHEN** a user uploads an Excel file to any file upload endpoint - **THEN** the system parses the file using ClosedXML and returns matching data ### Requirement: OpenAPI Documentation The system SHALL provide OpenAPI/Swagger documentation for all API endpoints. #### Business Rules - The system SHALL use Swashbuckle.AspNetCore for OpenAPI generation - The system SHALL expose Swagger UI at `/swagger` in development mode - The system SHALL document all endpoints with correct HTTP methods and response types - The system SHALL document authentication requirements for protected endpoints #### Scenario: Developer accesses API documentation - **WHEN** a developer navigates to `/swagger` in development mode - **THEN** the system displays interactive API documentation for all endpoints ### Requirement: JSON Serialization with System.Text.Json The system SHALL use System.Text.Json for all JSON serialization. #### Business Rules - The system SHALL NOT use Newtonsoft.Json (legacy) - The system SHALL use `JsonStringEnumConverter` for enum serialization - The system SHALL configure JSON options via `AddControllers().AddJsonOptions()` - The system SHALL return JSON responses (not redirects) for all API errors #### Scenario: Enum serialization in API response - **WHEN** an API endpoint returns a model with an enum property - **THEN** the system serializes the enum as a string (e.g., "Submitted" not 1) ### Requirement: IHubContext Dependency Injection The system SHALL use `IHubContext` for publishing SignalR updates from outside the hub. #### Business Rules - The system SHALL NOT use static `GlobalHost.ConnectionManager` pattern (legacy) - The system SHALL inject `IHubContext` into controllers and services - The system SHALL use `Clients.All.SendAsync()` for broadcasting - The system SHALL handle SignalR publish failures gracefully (log and continue) #### Scenario: Controller publishes search update - **WHEN** SearchController creates a new search - **THEN** the system uses injected `IHubContext` to broadcast the update ### Requirement: Async-First Pattern The system SHALL use async/await patterns for all I/O operations. #### Business Rules - All controller actions SHALL be async with `CancellationToken` parameter - All service methods SHALL be async with `CancellationToken` parameter - The system SHALL use `IAsyncEnumerable` for streaming scenarios where applicable - The system SHALL propagate `CancellationToken` to all downstream async calls #### Scenario: Cancellation during long-running operation - **WHEN** a client cancels a request during LDAP authentication - **THEN** the system throws `OperationCanceledException` and stops the operation ### Requirement: Structured Logging The system SHALL use `ILogger` for structured logging. #### Business Rules - The system SHALL NOT use NLog directly (legacy) - The system SHALL inject `ILogger` into all controllers and services - The system SHALL use structured logging with named parameters: `_logger.LogWarning("Failed to authenticate {Username}", username)` - The system SHALL log at appropriate levels (Information, Warning, Error) #### Scenario: Failed LDAP authentication logged - **WHEN** LDAP authentication fails - **THEN** the system logs a warning with structured username and error details ### Requirement: Options Pattern Configuration The system SHALL use `IOptions` for strongly-typed configuration. #### Business Rules - The system SHALL NOT use `ConfigurationManager` or `WebConfigurationManager` (legacy) - The system SHALL bind `LdapOptions` from `Ldap` configuration section - The system SHALL bind `AuthOptions` from `Auth` configuration section - The system SHALL inject `IOptions` or `IOptionsSnapshot` as needed #### Scenario: Configuration changes without restart - **WHEN** configuration values change in appsettings.json - **THEN** the system can use `IOptionsSnapshot` to read updated values ## MODIFIED Requirements ### Requirement: Cookie Authentication Events (Modified) The system SHALL suppress cookie authentication redirects and return HTTP status codes for Blazor WASM compatibility. #### Modified Business Rules - The system SHALL configure `OnRedirectToLogin` to return HTTP 401 instead of redirect - The system SHALL configure `OnRedirectToAccessDenied` to return HTTP 403 instead of redirect - The system SHALL return JSON error responses (not HTML) for authentication failures #### Scenario: Unauthenticated API request from Blazor - **WHEN** an unauthenticated Blazor WASM client requests a protected endpoint - **THEN** the system returns HTTP 401 with JSON error body (no redirect) ### Requirement: SignalR Hub Endpoint Path (Modified) The system SHALL map the StatusHub to the `/hubs/status` endpoint path. #### Modified Business Rules - The system SHALL map StatusHub to `/hubs/status` endpoint - The system SHALL configure SignalR with default settings (no custom protocols) #### Scenario: Client connects to SignalR hub - **WHEN** a Blazor WASM client connects to SignalR - **THEN** the system accepts connections at `/hubs/status` ### Requirement: Dependency Injected Memory Cache (Modified) The system SHALL use DI-injected `IMemoryCache` for file template caching. #### Modified Business Rules - The system SHALL inject `IMemoryCache` via constructor (not `MemoryCache.Default`) - The system SHALL use `MemoryCacheEntryOptions` with `AbsoluteExpirationRelativeToNow` - The system SHALL use `TryGetValue` pattern for type-safe cache retrieval #### Scenario: File template cached with DI cache - **WHEN** a file template is generated - **THEN** the system stores it in the injected `IMemoryCache` with 1-minute expiration ## Migration Notes | Legacy Pattern | New Pattern | Rationale | |----------------|-------------|-----------| | `System.DirectoryServices.DirectoryEntry` | `System.DirectoryServices.Protocols.LdapConnection` | Cross-platform compatibility | | EPPlus | ClosedXML | MIT license (EPPlus commercial in v7+) | | Newtonsoft.Json | System.Text.Json | Built-in, better performance | | `GlobalHost.ConnectionManager.GetHubContext()` | `IHubContext` via DI | Standard DI pattern, testable | | NLog | `ILogger` | Built-in logging abstraction | | `WebConfigurationManager.AppSettings` | `IOptions` | Strongly-typed configuration | | `MemoryCache.Default` | `IMemoryCache` via DI | Standard caching abstraction | | Login redirect on 401 | HTTP 401 response | Blazor WASM SPA compatibility |