mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-22 10:51:14 +00:00
[AZ-487] JWT validation baseline (HS256, all endpoints)
Adds Microsoft.AspNetCore.Authentication.JwtBearer 8.0.21 and the SatelliteProvider.Api.Authentication.AddSatelliteJwt extension that validates HS256 tokens against a shared JWT_SECRET (>=32 bytes, fail fast at startup). Every minimal-API endpoint now carries .RequireAuthorization(); the middleware chain is UseExceptionHandler -> UseHttpsRedirection -> UseCors -> UseAuthentication -> UseAuthorization -> endpoints. Swagger UI gets a Bearer security definition so the Authorize button works. Test infrastructure: JwtTokenFactory (unit) and JwtTestHelpers (integration) mint deterministic tokens against the same secret; the integration test runner attaches a default Bearer token to its shared HttpClient so existing tests continue to exercise protected endpoints. JwtIntegrationTests adds AC-1..AC-4 and AC-7 (Swagger advertises Bearer) end-to-end; AuthenticationServiceCollectionExtensionsTests covers AC-5 (missing/empty/short secret fail-fast) plus env-var precedence; JwtTokenFactoryTests covers AC-6 (claims pass through the JwtSecurityTokenHandler.ValidateToken path JwtBearer uses). docker-compose and scripts/run-tests.sh now propagate JWT_SECRET to the api and integration-tests containers, with a >=32-byte guard. .env.example documents the required keys; .env stays gitignored. Code review verdict: PASS_WITH_WARNINGS (2 Low findings surfaced in _docs/03_implementation/reviews/batch_01_cycle2_review.md). Cross-component coordination: gps-denied-onboard and the mission planner UI must attach Bearer tokens before this lands in dev. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,7 +22,14 @@ The three Layer-3 service components are compile-time siblings: each only refere
|
||||
- Single-instance deployment, no horizontal scaling requirements (`inferred-from: Channel-based queue, no distributed state`)
|
||||
- Append-by-source tile storage — multiple producers (Google Maps, UAV upload, future SatAR, …) can each persist a row per `(latitude, longitude, tile_zoom, tile_size_meters)` cell. Reads return the most-recent row across sources, ordered by `captured_at DESC` with deterministic `(updated_at DESC, id DESC)` tie-breaks. The single-row-per-cell-per-source invariant is enforced by the 5-column unique index `idx_tiles_unique_location_source` introduced in migration 013 (AZ-484). The `tiles.version` column is vestigial since AZ-357 dropped year-based cache invalidation in favour of cell-level overwrite. (`inferred-from: tiles table + AZ-484/AZ-357 migrations + tile-storage contract v1.0.0`)
|
||||
- Fire-and-forget async processing with status polling (`inferred-from: queue + background service + status endpoint`)
|
||||
- No authentication layer — designed as an internal/trusted network service (`inferred-from: no auth middleware in Program.cs`)
|
||||
- JWT-validated callers only — every HTTP endpoint requires a valid HS256-signed Bearer token, validated locally against a shared `JWT_SECRET` per the suite-level auth contract (`suite/_docs/10_auth.md`). Issuer/audience are intentionally not validated yet; signature + lifetime + ≥32-byte key are. Per-endpoint permission claims (e.g. `permissions: ["GPS"]` on the UAV upload) layer on top of this baseline.
|
||||
|
||||
**Authentication & Authorization** (AZ-487):
|
||||
- Validation library: `Microsoft.AspNetCore.Authentication.JwtBearer` 8.0.21 (matches the rest of the ASP.NET Core 8 package set).
|
||||
- Signing key: read from the `JWT_SECRET` environment variable (preferred) or the `Jwt:Secret` configuration key. Startup fails fast if the resolved secret is unset, empty, or shorter than 32 bytes (HMAC-SHA256 minimum per RFC 2104 §3).
|
||||
- Token contract: `ValidateIssuerSigningKey = true`, `ValidateLifetime = true`, `RequireSignedTokens = true`, `RequireExpirationTime = true`, `ValidateIssuer/Audience = false`, `ClockSkew = 30s`. The 5-minute JwtBearer default is intentionally tightened.
|
||||
- Authorization model: every endpoint registered in `Program.cs` is decorated with `.RequireAuthorization()`. AZ-488 adds `permissions`-claim policies on top of this baseline (UAV upload requires `GPS`).
|
||||
- Test infrastructure: `JwtTokenFactory` (unit tests) and `JwtTestHelpers` (integration tests) mint deterministic tokens against the same `JWT_SECRET`; the integration test runner attaches a default Bearer token to its shared `HttpClient` so legacy non-auth tests continue to exercise the protected endpoints unchanged.
|
||||
|
||||
**Planned features** (confirmed by user, currently stubs):
|
||||
- MGRS endpoint — tile access via Military Grid Reference System coordinates
|
||||
@@ -132,16 +139,16 @@ The N-source storage contract is authoritative in `_docs/02_document/contracts/d
|
||||
|
||||
## 7. Security Architecture
|
||||
|
||||
**Authentication**: None (internal service, no auth layer)
|
||||
**Authentication**: HS256 JWT Bearer tokens (AZ-487). Signing key from `JWT_SECRET` env var (≥ 32 bytes, validated at startup). `Microsoft.AspNetCore.Authentication.JwtBearer` validates signature, lifetime, and signing key; issuer and audience are intentionally not validated (suite contract does not specify expected values). ClockSkew tightened from JwtBearer default (5 min) to 30 s. Tokens are minted by the centralized Admin API per `suite/_docs/10_auth.md`.
|
||||
|
||||
**Authorization**: None (all endpoints are open)
|
||||
**Authorization**: Every endpoint requires authentication via `.RequireAuthorization()`. Permission-claim enforcement (e.g. `permissions: ["GPS"]`) is added per-endpoint where needed — AZ-488 introduces it on `POST /api/satellite/upload`. Other endpoints accept any authenticated principal.
|
||||
|
||||
**Data protection**:
|
||||
- At rest: No encryption (tiles stored as plain JPEG files)
|
||||
- In transit: HTTPS for Google Maps calls; API itself on HTTP
|
||||
- Secrets management: Google Maps session token in appsettings / env vars
|
||||
- In transit: HTTPS for Google Maps calls; API itself runs HTTP behind Kestrel (TLS termination is a deployment-layer concern)
|
||||
- Secrets management: `JWT_SECRET` and `GOOGLE_MAPS_API_KEY` from environment variables / `.env` (gitignored); `.env.example` documents the required keys. Production deployments MUST supply both via the host environment, never via the appsettings files.
|
||||
|
||||
**Audit logging**: Serilog writes to file; logs exceptions and processing state transitions
|
||||
**Audit logging**: Serilog writes to file; logs exceptions and processing state transitions. 401/403 responses are emitted by the JwtBearer middleware via the `WWW-Authenticate` header; no body leakage of internal details.
|
||||
|
||||
## 8. Key Architectural Decisions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user