refactor: enhance JWT authentication and CORS configuration

Updated JWT authentication to use configuration values instead of hardcoded secrets, improving security and flexibility. Enhanced CORS policy to conditionally allow origins based on configuration settings, with logging for permissive defaults. Updated README to reflect project renaming and clarify service context.
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-14 19:48:25 +03:00
parent 2fe394d732
commit 7025f4d075
74 changed files with 8494 additions and 19 deletions
+63
View File
@@ -0,0 +1,63 @@
# Module: `Azaion.Missions.Middleware`
> **NOTE (forward-looking)**: post-rename namespace. Today's source still lives under `Azaion.Flights.Middleware`. Renames tracked under Jira AZ-EPIC child B5.
**Files (1)**: `Middleware/ErrorHandlingMiddleware.cs`
## Purpose
Global exception → JSON error response mapper. Wraps the rest of the request pipeline and converts a fixed set of exception types to specific HTTP status codes; everything else becomes a 500.
## Public Interface
```csharp
public class ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger) {
public Task Invoke(HttpContext context);
}
```
Standard ASP.NET Core middleware shape (primary-constructor variant).
## Internal Logic
```text
try { await next(context); }
catch (KeyNotFoundException ex) → 404 NotFound, body: { statusCode, message = ex.Message }
catch (ArgumentException ex) → 400 BadRequest, body: { statusCode, message = ex.Message }
catch (InvalidOperationException ex) → 409 Conflict, body: { statusCode, message = ex.Message }
catch (Exception ex) → 500 InternalServerError, body: { statusCode, message = "Internal server error" }
PLUS: logger.LogError(ex, "Unhandled exception");
```
Body is serialized via `JsonSerializer.Serialize(new { statusCode, message })` — an **anonymous object literal**, NOT the `DTOs.ErrorResponse` type. The anonymous-type property names are written lowercase-first in code (`statusCode`, `message`); `System.Text.Json` preserves them as-is when no `JsonNamingPolicy` is configured, so the wire shape is `{"statusCode":..., "message":"..."}`**camelCase by accidental match with the suite spec**. (The unused `DTOs.ErrorResponse` type, by contrast, declares PascalCase properties and would serialize PascalCase if it were ever used directly.)
`Content-Type` is set to `application/json`. Response body is written via `WriteAsync` (string).
## Dependencies
- `System.Net` (`HttpStatusCode`)
- `System.Text.Json` (`JsonSerializer`)
- ASP.NET Core middleware abstractions (transitive via `Microsoft.NET.Sdk.Web`)
- `Microsoft.Extensions.Logging.ILogger<T>` (transitive)
No internal dependencies.
## Consumers
- `Program.cs``app.UseMiddleware<ErrorHandlingMiddleware>();` is called BEFORE `UseCors`, `UseAuthentication`, `UseAuthorization`, `UseSwagger`, `UseSwaggerUI`, `MapControllers`.
## Configuration / External Integrations / Security
None.
## Tests
None present.
## Notes / Smells
1. **`DTOs.ErrorResponse` is unused here** — middleware writes an anonymous object instead. **Partial spec divergence**: `../../suite/_docs/00_top_level_architecture.md` § Error Response Format mandates `{ "statusCode", "message", "errors": object? }` (camelCase, `errors` is an *object* of per-field arrays). The middleware's anonymous-object output IS already camelCase on case (`{"statusCode":..., "message":"..."}`) but missing the `errors` field; the static `ErrorResponse` DTO has the wrong shape (`List<string>? Errors` instead of `object?`) and is dead code.
2. **Entity / DTO body case-style** is PascalCase across the rest of the API (controller responses serialize entities and `PaginatedResponse<T>` via `System.Text.Json` defaults with no naming policy override). The error envelope's accidental camelCase match documented in point 1 does NOT extend to those responses — see `architecture.md` ADR-002.
3. **`InvalidOperationException` → 409 Conflict** is a non-standard mapping. The .NET BCL throws this for many "wrong state at the moment" conditions; in this codebase it is used by `VehicleService.DeleteVehicle` to report "vehicle is referenced by missions" (a true 409). But any third-party library throwing `InvalidOperationException` for an unrelated reason would also surface as 409, masking the real cause.
4. **Generic 500 swallows the message** (good for security — no internal detail leaked) and logs the exception (good for diagnostics). No correlation ID is included in the response, so production support has to grep logs by timestamp.
5. **Order of catches** matters: `ArgumentException` is base of `ArgumentNullException` / `ArgumentOutOfRangeException`, so those also become 400 (correct). `InvalidOperationException` ahead of generic `Exception` is correct.