Files
annotations/_docs/cross-repo/flights_h1_h2_h3_change_spec.md
Oleksandr Bezdieniezhnykh 03f879206e docs+src: complete Steps 1-3 outcomes + auth re-sync baseline
This commit captures everything produced during autodev existing-code
Steps 1 (Document), 2 (Architecture Baseline Scan), and 3 (Test Spec),
together with the targeted auth + CORS re-sync triggered on 2026-05-14
when codebase drift was detected at Step 4 entry. None of this work was
previously committed.

Step 1 (Document) — 50+ _docs/02_document/ files: problem, solution,
architecture, system flows, glossary, module-layout, per-component
specs (01..06), modules, deployment, diagrams, data model, FINAL
report, verification log, discovery.

Step 2 (Architecture Baseline) — architecture_compliance_baseline.md.
Verdict PASS_WITH_WARNINGS (0 Critical, 0 High, 1 Medium, 2 Low). No
High/Critical findings; auto-chained to Step 3 per existing-code flow.

Step 3 (Test Spec) — _docs/02_document/tests/* (67 scenarios across
blackbox, security, resilience, resource-limit, performance), plus
e2e/docker-compose.test.yml, e2e/seed/run.sh, scripts/run-tests.sh,
scripts/run-performance-tests.sh. Coverage 88% over the active scope
(40 of 45 items covered, 6 RB-deferred, 5 documented-as-uncovered).

Targeted auth + CORS re-sync — replaces the deleted in-house token
issuer with a JWKS-verifier model. AuthController and TokenService
removed; JwtExtensions switched from HS256 symmetric to ES256 over
admin's JWKS. ConfigurationResolver and CorsConfigurationValidator
added under src/Infrastructure/. ADR-002 and ADR-006 retired; SEC-01,
SEC-02, SEC-03 marked Closed. One new testability risk recorded in
architecture.md Open Risks Section 6 (JWKS HTTPS gating).

Source changes:
- src/Auth/JwtExtensions.cs (modified) — ES256, JWKS, alg pinning
- src/Program.cs (modified) — DI wiring for ConfigurationResolver
  and CorsConfigurationValidator
- src/Controllers/AuthController.cs (deleted) — no in-service issuance
- src/Services/TokenService.cs (deleted) — same
- src/Infrastructure/ConfigurationResolver.cs (new)
- src/Infrastructure/CorsConfigurationValidator.cs (new)
- .env.example (new) — required env var documentation
- .gitignore (updated)

Cross-repo coordination: _docs/cross-repo/flights_h1_h2_h3_change_spec
captures the change-spec for downstream services that consumed the now
deleted /auth endpoints.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 20:19:05 +03:00

5.1 KiB

Flights — parallel H1/H2/H3 change spec

Drop-in equivalent of the H1/H2/H3 fixes landed in annotations/ this cycle. The workspace boundary rule (.cursor/rules/workspace-boundary.mdc) prevents this agent from editing the flights/ repo directly; this document is the contract the flights workspace should implement on its own branch.

Source of truth for the new files / patterns is the annotations workspace:

  • annotations/src/Auth/JwtExtensions.cs — JWKS verifier wiring.
  • annotations/src/Infrastructure/ConfigurationResolver.cs — fail-fast env-var helper.
  • annotations/src/Infrastructure/CorsConfigurationValidator.cs — CORS allow-list guard.
  • annotations/src/Program.cs — composition root.

The flights changes are byte-equivalent except for the items called out under "Differences" below.

H1 — JWKS verifier (replace HS256 shared secret)

In flights/Auth/JwtExtensions.cs:

  • Replace AddJwtAuth(string jwtSecret) with AddJwtAuth(IConfiguration configuration).
  • Resolve JWT_ISSUER, JWT_AUDIENCE, JWT_JWKS_URL via the new ConfigurationResolver.ResolveRequiredOrThrow helper (no fallbacks).
  • Build a ConfigurationManager<JsonWebKeySet> over a minimal IConfigurationRetriever<JsonWebKeySet> (admin only exposes JWKS, not the full OIDC discovery doc — copy the JwksRetriever private class verbatim from annotations).
  • TokenValidationParameters must be:
    • ValidateIssuer = true, ValidIssuer = issuer
    • ValidateAudience = true, ValidAudience = audience
    • ValidateLifetime = true, ValidateIssuerSigningKey = true
    • ValidAlgorithms = [SecurityAlgorithms.EcdsaSha256] (pinned)
    • RequireSignedTokens = true, RequireExpirationTime = true
    • ClockSkew = TimeSpan.FromSeconds(30)
    • IssuerSigningKeyResolver returns jwks.GetSigningKeys() filtered by kid.
  • Keep the existing authorization policies in place (FL, GPS).

H2 — fail-fast env vars (drop insecure defaults)

In flights/Program.cs:

  • Delete ?? "Host=localhost;Database=azaion;Username=postgres;Password=changeme" for DATABASE_URL and resolve it through ConfigurationResolver.ResolveRequiredOrThrow.
  • Delete ?? "development-secret-key-min-32-chars!!" for JWT_SECRET and remove the variable entirely (AddJwtAuth now takes IConfiguration).

H3 — config-driven CORS allow-list

In flights/Program.cs:

  • Read CorsConfig:AllowedOrigins (string array) and CorsConfig:AllowAnyOrigin (bool).
  • Call CorsConfigurationValidator.EnsureSafeForEnvironment(...) before AddCors. In Production with empty origins and AllowAnyOrigin=false, throw.
  • Build the default policy with WithOrigins(allowedOrigins) (locked) or AllowAnyOrigin() (permissive opt-in) per ShouldUsePermissivePolicy.
  • After builder.Build(), log a warning when running with the permissive default in a non-Production environment (ShouldWarnAboutPermissiveDefault).

Copy CorsConfigurationValidator.cs verbatim, only changing the namespace to Azaion.Flights.Infrastructure.

Side-effect: local token minting

If flights has its own Services/TokenService.cs or Controllers/AuthController.cs that mints tokens with HS256 (matching the pattern annotations had before this cycle), it MUST be removed; otherwise the new validator (ValidAlgorithms pinned to EcdsaSha256) will reject the locally-minted tokens at the next [Authorize] hop. Admin is the sole token issuer for the suite after this change.

If flights had no local token minting before, this section does not apply.

Differences from annotations

  • Authorization policies in JwtExtensions: keep flights' existing FL and GPS policies; do NOT add annotations' ANN/DATASET/ADM policies.
  • Namespace prefix: Azaion.Flights instead of Azaion.Annotations.

.env.example (new file)

Mirror annotations' template; required keys:

DATABASE_URL=
JWT_ISSUER=AzaionApi
JWT_AUDIENCE=Annotators/OrangePi/Admins
JWT_JWKS_URL=https://admin.azaion.com/.well-known/jwks.json
# CorsConfig__AllowedOrigins__0=https://...
# CorsConfig__AllowAnyOrigin=false

Confirm the Issuer / Audience values against the production admin deployment before merging.

Docs to update in flights/_docs/

  • 02_document/modules/auth-identity.md (or equivalent) — verifier-only role, remove any HS256 references, document the JWKS resolver wiring.
  • 02_document/deployment/environment_strategy.md (or equivalent) — required-vs-optional env table; remove JWT_SECRET, add the three new JWT vars and the CORS config keys.
  • 02_document/architecture.md (or equivalent) — retire any ADRs that pinned HS256 / wide-open CORS.

Verification before merge

  1. dotnet build succeeds.
  2. Manually unset JWT_ISSUER (or JWT_AUDIENCE, JWT_JWKS_URL, DATABASE_URL) and confirm startup throws InvalidOperationException with a helpful message naming the env var.
  3. With ASPNETCORE_ENVIRONMENT=Production and no CorsConfig:AllowedOrigins, confirm startup throws.
  4. With a valid admin-issued ES256 token, confirm [Authorize] endpoints return 200.
  5. With a token forged using alg=HS256 and admin's public key as the HMAC secret, confirm the endpoint returns 401 (alg-confusion attack rejected).