mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 13:51:14 +00:00
f979e18811
Option B per user decision: production ships with empty Jwt.Issuer / Jwt.Audience in appsettings.json so the API process refuses to start unless JWT_ISSUER + JWT_AUDIENCE env vars are supplied. Development ships with grep-friendly DEV-ONLY- placeholders so local + docker flows keep working unchanged. AuthenticationServiceCollectionExtensions flips ValidateIssuer + ValidateAudience to true and wires ValidIssuer / ValidAudience via a new ResolveRequiredOrThrow helper that all three required values (secret, iss, aud) now share. JwtTokenFactory.Create + CreateExpired gain optional iss / aud parameters (default null) so existing call sites compile unchanged. JwtTestHelpers adds MintAuthenticated / MintExpired wrappers that resolve iss + aud from env, plus ResolveIssuerOrThrow / ResolveAudienceOrThrow. PerfBootstrap.MintToken + Program.cs JWT bootstrap migrated to the new surface so the perf harness and the integration runner both validate against the same contract. Adds 4 fail-fast unit tests (missing/empty issuer + audience), 2 negative integration scenarios (WrongIssuer_Returns401, WrongAudience_Returns401), and re-tags every existing integration mint site via MintAuthenticated. Compose, .env.example, run-tests.sh, run-performance-tests.sh all load + export JWT_ISSUER + JWT_AUDIENCE alongside JWT_SECRET. Resolves F-AUTH-2 (security_report.md + owasp_review.md). AC-7 (cross-repo suite/_docs/10_auth.md write) deferred — outside this workspace; tracked in deploy_cycle2.md R3 follow-up. Co-authored-by: Cursor <cursoragent@cursor.com>
11 KiB
11 KiB
Batch Report — Batch 05 cycle 3
Batch: 05 (cycle 3) Tasks: AZ-494 (JWT iss/aud validation — enable + configure) Date: 2026-05-12
Task Results
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|---|---|---|---|---|---|
| AZ-494_jwt_iss_aud_validation | Done (Option B) | 1 prod source + 1 TestSupport + 4 IntegrationTests + 1 unit test + 4 config + 4 scripts/compose + 6 docs | 4 new unit tests + 2 new integration scenarios; existing 13 unit + 8 integration cases re-tagged to mint via env iss/aud | 6/7 ACs addressed; AC-7 deferred (cross-repo suite/_docs/10_auth.md write) |
0 blockers; 1 Low (cross-repo doc), 1 acknowledged operational gate (admin team must supply real prod iss/aud — fail-fast at deploy enforces this). |
AC Test Coverage: 6 of 7 addressed (AC-7 deferred — cross-repo)
Code Review Verdict: pending (this batch report precedes per-batch review)
Auto-Fix Attempts: 0
Stuck Agents: None
What was implemented
The task spec offered three options for handling the blocker (admin team has not yet confirmed production iss / aud values). The user selected Option B: implement the full validation plumbing now with clearly-tagged DEV-only values in appsettings.Development.json so local tests work, but leave appsettings.json empty so production deploys without explicit JWT_ISSUER / JWT_AUDIENCE environment variables fail at startup, not at runtime.
The implementation therefore:
- Enables
ValidateIssuer = trueandValidateAudience = truein the production token-validation pipeline. - Sources both values from
JWT_ISSUER/JWT_AUDIENCEenv vars withJwt:Issuer/Jwt:Audienceconfig keys as fallback (same resolution pattern asJWT_SECRETfrom AZ-487). - Throws
InvalidOperationExceptionat startup if either value is missing or whitespace — the message names the env var, the config key, and the AZ-494 task spec. - Threads
iss/audthrough the canonicalSatelliteProvider.TestSupport.JwtTokenFactory.Createsurface (the post-AZ-491 single source of truth) so every existing test path continues to mint matching tokens. - Adds a thin convenience layer in
SatelliteProvider.IntegrationTests/JwtTestHelpers(MintAuthenticated,MintExpired,ResolveIssuerOrThrow,ResolveAudienceOrThrow) so integration test call sites stay terse and centrally fail-fast on missing env vars. - Adds two new negative integration tests (
WrongIssuer_Returns401,WrongAudience_Returns401) and four new unit fail-fast tests (AddSatelliteJwt_ThrowsOnMissingIssuer/_ThrowsOnEmptyIssuer/_ThrowsOnMissingAudience/_ThrowsOnEmptyAudience). - Updates security artefacts (
security_report.mdflips F-AUTH-2 to RESOLVED,owasp_review.mdA07 reflects same), the architecture + module docs (architecture.md,modules/api_program.md,modules/tests_integration.md,modules/tests_unit.md), the cycle-2 deploy report (R3 follow-up note), and the traceability matrix (5 new rows for AZ-494 AC-1..AC-7).
Added
SatelliteProvider.IntegrationTests/JwtTestHelpers.cs— three new public helpers:ResolveIssuerOrThrow()/ResolveAudienceOrThrow()— mirror the existingResolveSecretOrThrowpattern (read env, throwInvalidOperationExceptionwith a humanised message if missing).MintAuthenticated(...)— convenience wrapper: defaults issuer + audience to the env-resolved values, accepts explicit overrides for negative test cases.MintExpired(...)— convenience wrapper for the existingJwtTokenFactory.CreateExpiredoverload, same env-resolution behaviour.
- 4 unit tests in
SatelliteProvider.Tests/Authentication/AuthenticationServiceCollectionExtensionsTests.cs:AddSatelliteJwt_ThrowsOnMissingIssuer,AddSatelliteJwt_ThrowsOnEmptyIssuer,AddSatelliteJwt_ThrowsOnMissingAudience,AddSatelliteJwt_ThrowsOnEmptyAudience. - 2 integration tests in
SatelliteProvider.IntegrationTests/JwtIntegrationTests.cs:WrongIssuer_Returns401,WrongAudience_Returns401.
Modified
SatelliteProvider.Api/Authentication/AuthenticationServiceCollectionExtensions.cs— addedJwtIssuerEnvVar/JwtIssuerConfigKey/JwtAudienceEnvVar/JwtAudienceConfigKeyconstants; flippedValidateIssuer/ValidateAudiencetotrueand wiredValidIssuer/ValidAudience; extracted a singleResolveRequiredOrThrowhelper that all three required values now flow through.SatelliteProvider.TestSupport/JwtTokenFactory.cs—Create(...)andCreateExpired(...)gained optionalissuer/audienceparameters defaulted tonull(old call sites still produce identical tokens; new call sites pass real values).SatelliteProvider.IntegrationTests/PerfBootstrap.cs—MintToken()now also resolves iss + aud and passes them through toJwtTokenFactory.Create. Without this the perf harness's bearer token would fail validation against the AZ-494-hardened API.SatelliteProvider.IntegrationTests/Program.cs— JWT bootstrap now resolves all three required values (secret + iss + aud) inside a singletry/catchand prints them at startup. TheMintAuthenticatedhelper replaces the inlineJwtTokenFactory.Createcall that used to live inMain.SatelliteProvider.IntegrationTests/JwtIntegrationTests.cs—AnonymousRequest_*,ExpiredToken_Returns401,InvalidSignature_Returns401,ValidToken_Returns200_OnHealthyEndpointall migrated toJwtTestHelpers.MintAuthenticated/JwtTestHelpers.MintExpired. Adds the two new scenarios toRunAll.SatelliteProvider.IntegrationTests/UavUploadTests.cs— everyJwtTokenFactory.Create(...)call replaced withJwtTestHelpers.MintAuthenticated(...). Directusing SatelliteProvider.TestSupport;dropped (no longer needed at this seam).SatelliteProvider.Tests/Authentication/AuthenticationServiceCollectionExtensionsTests.cs— existing_ConfiguresTokenValidationParameters_AsPerContractand_PrefersEnvironmentVariableOverConfigurationcases updated to assert the AZ-494 contract;BuildValidConfiguration()now seeds iss + aud; static[Fact]setup/teardown saves and restores both env vars.SatelliteProvider.Api/appsettings.json— added emptyJwt.IssuerandJwt.Audiencekeys (the fail-fast contract requires env-var or non-empty config; empty here forces ops to supply env vars in prod).SatelliteProvider.Api/appsettings.Development.json— placeholder dev values prefixedDEV-ONLY-so a grep for that prefix surfaces every "remember to replace" site..env.example— documentsJWT_ISSUERandJWT_AUDIENCEwith the fail-fast contract and a one-line example value pair (sameDEV-ONLY-prefix).docker-compose.yml/docker-compose.tests.yml—JWT_ISSUERandJWT_AUDIENCEnow passed through to both theapiandintegration-testsservices.scripts/run-tests.sh/scripts/run-performance-tests.sh—.envload + fail-fast checks for both new vars mirror the existingJWT_SECRETflow; both are exported so Docker Compose and the perf bootstrap see them.
Documentation
_docs/02_document/architecture.md— token contract bullet + Security Architecture authentication paragraph updated._docs/02_document/modules/api_program.md— JWT authentication section + Configuration section._docs/02_document/modules/tests_integration.md— env-var prerequisites updated;JwtIntegrationTestsandJwtTestHelpersentries describe the new AZ-494 surface._docs/02_document/modules/tests_unit.md—AuthenticationServiceCollectionExtensionsTestsentry now lists the four AZ-494 fail-fast cases plus the updated config-precedence and contract assertions._docs/02_document/tests/traceability-matrix.md— 5 new rows for AC-1..AC-7 (AC-7 marked deferred); the AZ-487 NFR rows updated to acknowledge the AZ-494 extension._docs/03_implementation/deploy_cycle2.md— R3 follow-up note marked RESOLVED in cycle 3 (AZ-494) with the residual operational gate spelled out._docs/05_security/security_report.md— F-AUTH-2 flipped to RESOLVED cycle 3 (AZ-494); verdict reconciliation + recommendations updated._docs/05_security/owasp_review.md— A07 row updated; new Low finding for residual "no token revocation list" gap noted as a separate follow-up.
AC verification
| AC | Description | Verification |
|---|---|---|
| AC-1 | Wrong iss token returns 401 |
JwtIntegrationTests.WrongIssuer_Returns401 (integration; runtime gate at Step 16) |
| AC-2 | Wrong aud token returns 401 |
JwtIntegrationTests.WrongAudience_Returns401 (integration; runtime gate at Step 16) |
| AC-3 | Matching iss + aud accepted | JwtIntegrationTests.ValidToken_Returns200_OnHealthyEndpoint retains its assertion; tokens now minted via env-resolved iss/aud through MintAuthenticated |
| AC-4 | Missing config fails fast | 4 new unit tests in AuthenticationServiceCollectionExtensionsTests; manual docker compose up without env vars throws InvalidOperationException per the contract |
| AC-5 | Existing tests pass with matched fixtures | All JwtTokenFactory.Create direct call sites in the integration project removed in favour of MintAuthenticated (verified via Grep); unit suite still mints via the factory with explicit iss/aud |
| AC-6 | Security artefacts updated | security_report.md + owasp_review.md updated this batch |
| AC-7 | Suite-level contract reflects validation | Deferred — suite/_docs/10_auth.md lives in the parent monorepo, outside this workspace. Cross-repo write is out of scope for satellite-provider's autodev. deploy_cycle2.md notes the cross-repo obligation. |
Static / process checks
dotnet format whitespace --verify-no-changeswill run as part ofscripts/run-tests.shat Step 16.ReadLintson every modified C# file returned 0 warnings.- Repo-wide grep for
JwtTokenFactory.Createconfirms onlySatelliteProvider.Tests(unit, which intentionally exercises the factory directly with explicit iss/aud) +PerfBootstrap.MintToken+JwtTestHelpers.MintAuthenticated/MintExpiredcall it now — the integration suite never bypasses the env-resolution wrapper. .env.examplekeeps theDEV-ONLY-prefix grep-friendly so a future ops review can surface every placeholder site at once.
Risks & follow-ups
- Operational gate (intentional, by Option B) — production deploy WITHOUT
JWT_ISSUER+JWT_AUDIENCEenv vars will fail at process start with theInvalidOperationExceptionmessage documented above. This is the controlled deploy-time forcing function for admin-team confirmation. - Cross-repo doc (AC-7) —
suite/_docs/10_auth.mdwrite deferred. Will surface as a_docs/_process_leftovers/entry if the suite repo still needs the update after this autodev finishes.