mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 17:41:09 +00:00
[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>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# 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-detection
|
||||
- `SessionService` — revocation paths and the verifier-poll snapshot
|
||||
- `MissionTokenService` — inserts mission-class rows
|
||||
- `AzaionDb.Sessions` — `ITable<Session>` access
|
||||
- `AzaionDbSchemaHolder` — maps `Session` to the `sessions` table
|
||||
|
||||
## 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_hash` stores SHA-256 of the opaque token; the plaintext is never persisted.
|
||||
- The `family_id` partial index `sessions_family_active_idx WHERE revoked_at IS NULL` keeps reuse-detection and `RevokeAllForUser` cheap 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 index `sessions_aircraft_active_idx (aircraft_id, class) WHERE revoked_at IS NULL AND aircraft_id IS NOT NULL` keeps that check O(active mission rows).
|
||||
|
||||
## Tests
|
||||
|
||||
Indirectly tested via `RefreshTokenTests`, `LogoutTests`, `MissionTokenTests`, and `MfaLoginTests` (which all exercise the entity through the service layer).
|
||||
Reference in New Issue
Block a user