Files
admin/_docs/02_document/modules/common_configs_jwt_config.md
T
Oleksandr Bezdieniezhnykh a77b3f8a59 [AZ-529] [AZ-530] Cycle-2 documentation refresh
Refreshes _docs/02_document/ to reflect the cycle-2 auth-modernization
+ CMMC hardening landings (AZ-531..AZ-538). Authoritative source for
the ripple set is ripple_log_cycle2.md.

Covered:
- architecture.md (section 1 rewritten, ADRs 6-9 added)
- data_model.md (sessions, audit_events, user columns, migrations)
- system-flows.md (F1 rewritten; F11-F17 added; F2/F7/F9 minor)
- module-layout.md (cycle-2 sub-component table)
- diagrams/flows/flow_login.md (dual-token + MFA)
- components/{01_data_layer,03_auth_and_security,05_admin_api}
- modules/ (12 new, 8 modified — full Argon2id/ES256/MFA/refresh
  /mission/session/audit/jwks rollup)
- tests/{blackbox,security,traceability-matrix}

Step 13 (Update Docs) output for cycle 2.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 09:22:53 +03:00

3.9 KiB

Module: Azaion.Common.Configs.JwtConfig + SessionConfig

Purpose

Configuration POCOs for JWT signing/validation and refresh-token TTLs. Bound from appsettings.json sections JwtConfig and SessionConfig. Both classes live in Azaion.Common/Configs/JwtConfig.cs.

Cycle 2 (2026-05-14) note (AZ-531 / AZ-532) — major reshape:

  • HS256 shared-secret signing is gone. Secret is no longer read by any code path; the property is retained only as a temporary rollback escape hatch (AZ-532 spec).
  • New: KeysFolder (PEM directory) and ActiveKid (currently-signing key id) for ES256.
  • New: AccessTokenLifetimeMinutes (default 15) replaces the old TokenLifetimeHours (default 4) — short-lived access tokens are now paired with refresh-token rotation.
  • New companion class SessionConfig carries refresh-token TTLs.

Public Interface

JwtConfig

Property Type Default Description
Issuer string (required) Token iss claim. Validated by JwtBearer middleware.
Audience string (required) Token aud claim for interactive sessions. (Mission tokens override to satellite-provider; MFA step-1 tokens override to azaion-mfa-step2.)
KeysFolder string secrets/jwt-keys Directory containing one ES256 PEM per key. The kid is the filename without .pem.
ActiveKid string? null Kid currently used to sign new tokens. If null, falls back to the first PEM by ordinal filename order with a startup log warning.
AccessTokenLifetimeMinutes int 15 Access-token TTL.

SessionConfig

Property Type Default Description
RefreshSlidingHours int 8 Each rotation extends expires_at by this many hours from now.
RefreshAbsoluteHours int 12 Family is rejected past this many hours since family_started_at, regardless of sliding rotations.

Internal Logic

None — pure data classes.

Dependencies

None.

Consumers

  • Program.cs
    • reads JwtConfig eagerly to fail-fast on missing Issuer/Audience and to construct the JwtSigningKeyProvider before app.Build()
    • registers Configure<JwtConfig> and Configure<SessionConfig> for downstream injection
  • JwtSigningKeyProvider — reads KeysFolder, ActiveKid
  • AuthService.CreateToken — reads Issuer, Audience, AccessTokenLifetimeMinutes
  • RefreshTokenService — reads SessionConfig.RefreshSlidingHours, RefreshAbsoluteHours
  • MfaService.IssueMfaStepToken / ValidateMfaStepToken — reads Issuer (audience is hard-coded to azaion-mfa-step2)
  • MissionTokenService.MintToken — reads Issuer (audience is hard-coded to satellite-provider)

Data Models

None.

Configuration

Bound via builder.Configuration.GetSection(nameof(JwtConfig)) and Configure<SessionConfig>. Override via env vars:

  • JwtConfig__Issuer=…, JwtConfig__Audience=…, JwtConfig__KeysFolder=/var/lib/azaion/jwt-keys, JwtConfig__ActiveKid=kid-2026-05-14
  • SessionConfig__RefreshSlidingHours=8, SessionConfig__RefreshAbsoluteHours=12

External Integrations

Filesystem (read-only on KeysFolder).

Security

  • Private signing keys live on disk only; the JWKS endpoint exports only public components. chmod 600 is applied by scripts/generate-jwt-key.sh.
  • The legacy Secret field is retained but unused; remove on a follow-up cleanup ticket once the rollback window has closed.
  • RefreshAbsoluteHours is the hard cap on session lifetime — no rotation can extend past it. Bumping above 12 h needs a security review because it directly extends the leak-window of any one refresh token.

Tests

  • e2e/Azaion.E2E/Tests/JwksTests.cs — exercises the rotation overlap (AC-3) by manipulating KeysFolder and ActiveKid.
  • e2e/Azaion.E2E/Tests/RefreshTokenTests.cs — exercises both the sliding and absolute caps (AC-4).