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>
4.3 KiB
Module: Azaion.Common.Entities.Session
Purpose
Domain entity representing one issued refresh token (interactive sessions) or one mission token (long-lived UAV sessions). One row per issued token; rotated rows chain via ParentSessionId and share a FamilyId so reuse-detection and family-wide revocation can key off it.
Added in cycle 2 (2026-05-14). Initial shape from AZ-531 (interactive refresh-token sessions); extended in the same cycle by AZ-535 (
RevokedByUserId), AZ-533 (Class,AircraftId), and AZ-534 (MfaAuthenticated).
Public Interface
Session
| Property | Type | Description |
|---|---|---|
Id |
Guid |
Primary key. |
UserId |
Guid |
FK to users.id. |
RefreshHash |
string? |
SHA-256 hex of the opaque refresh token. NULL for mission sessions (they have no refresh value). Unique-indexed. |
FamilyId |
Guid |
All rotations of the same login share this id. For interactive root rows and for mission rows, FamilyId == Id. |
IssuedAt |
DateTime |
Row creation time. |
LastUsedAt |
DateTime |
Updated on rotation; informational. |
ExpiresAt |
DateTime |
Sliding (interactive) or absolute (mission) expiry. |
RevokedAt |
DateTime? |
Set on rotation, reuse-detection, logout, admin revoke, post-flight reconnect. |
RevokedReason |
string? |
One of SessionRevokedReasons. |
ParentSessionId |
Guid? |
The previous row in the family (set on rotation). |
FamilyStartedAt |
DateTime |
First-issue time of the family — used for the absolute expiry check. |
RevokedByUserId |
Guid? |
AZ-535 — audit trail of who revoked the session. NULL for system revocations (rotation, reuse, post-flight). |
Class |
string |
AZ-533 — "interactive" (default) or "mission". |
AircraftId |
Guid? |
AZ-533 — for mission sessions, the CompanionPC user the mission token belongs to. Used by RevokeMissionsForAircraft. |
MfaAuthenticated |
bool |
AZ-534 — pinned at issue; refresh rotation inherits the original AMR strength even if MFA is enabled/disabled mid-session. |
SessionRevokedReasons (constants)
| Value | When |
|---|---|
rotated |
Old row marked as superseded by a successful refresh rotation. |
reuse_detected |
OAuth 2.1 §6.1 — already-rotated refresh re-presented; whole family killed. |
logged_out |
User called POST /logout. |
logged_out_all |
User called POST /logout/all. |
admin_revoked |
Admin called POST /sessions/{sid}/revoke. |
post_flight_reconnect |
Aircraft reconnected; mission auto-revoked. |
family_revoked |
Reserved (manual family-wide revocation; not currently emitted). |
SessionClasses (constants)
| Value | Meaning |
|---|---|
interactive |
Refresh-backed user session (AZ-531 default). |
mission |
Long-lived no-refresh UAV mission token (AZ-533). |
Internal Logic
None — pure data class. All session lifecycle logic lives in RefreshTokenService, SessionService, MissionTokenService.
Dependencies
None.
Consumers
RefreshTokenService— inserts root/family rows, updates on rotation/reuse-detectionSessionService— revocation paths and the verifier-poll snapshotMissionTokenService— inserts mission-class rowsAzaionDb.Sessions—ITable<Session>accessAzaionDbSchemaHolder— mapsSessionto thesessionstable
Data Models
Maps to PostgreSQL table sessions (defined in env/db/08_sessions.sql, extended by 09_sessions_logout_and_mission.sql and 10_users_mfa.sql).
Configuration
None.
External Integrations
None.
Security
refresh_hashstores SHA-256 of the opaque token; the plaintext is never persisted.- The
family_idpartial indexsessions_family_active_idx WHERE revoked_at IS NULLkeeps reuse-detection andRevokeAllForUsercheap even as the revoked tail grows. - Auto-revoke-on-reconnect (
RevokeMissionsForAircraft) closes the mission-token "lost UAV" risk when the aircraft phones home again; the partial indexsessions_aircraft_active_idx (aircraft_id, class) WHERE revoked_at IS NULL AND aircraft_id IS NOT NULLkeeps that check O(active mission rows).
Tests
Indirectly tested via RefreshTokenTests, LogoutTests, MissionTokenTests, and MfaLoginTests (which all exercise the entity through the service layer).