Files
jdescopingtool/openspec/changes/archive/2026-01-01-implement-web-api/specs/web-api-auth/spec.md
T
Joseph Doherty 26ff8d9b4f Initial commit: JDE Scoping Tool migration project
Set up repository with legacy .NET Framework 4.8 source (OLD/),
new .NET 10 Blazor solution (NEW/), OpenSpec specifications,
documentation, and project configuration.
2026-01-02 07:43:29 -05:00

8.2 KiB

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<StatusHub> 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<StatusHub> 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<StatusHub> 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<T> for structured logging.

Business Rules

  • The system SHALL NOT use NLog directly (legacy)
  • The system SHALL inject ILogger<T> 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<T> 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<T> or IOptionsSnapshot<T> as needed

Scenario: Configuration changes without restart

  • WHEN configuration values change in appsettings.json
  • THEN the system can use IOptionsSnapshot<T> to read updated values

MODIFIED Requirements

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<T> 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<T>() IHubContext<T> via DI Standard DI pattern, testable
NLog ILogger<T> Built-in logging abstraction
WebConfigurationManager.AppSettings IOptions<T> Strongly-typed configuration
MemoryCache.Default IMemoryCache via DI Standard caching abstraction
Login redirect on 401 HTTP 401 response Blazor WASM SPA compatibility