mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 12:51:09 +00:00
a77b3f8a59
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
4.3 KiB
Module: Azaion.Services.SessionService
Purpose
Logout / revocation surface and the verifier-poll snapshot. Distinct from RefreshTokenService (which rotates and reuse-detects); this service expresses the human / admin / system intent to kill a session and exposes the cross-service denylist feed.
Added in cycle 2 (2026-05-14) by AZ-535 (Epic AZ-529). The
RevokeMissionsForAircraftpath was added the same day for AZ-533 (mission-token auto-revoke on reconnect).
Public Interface
ISessionService
| Method | Signature | Description |
|---|---|---|
RevokeBySid |
Task<bool> RevokeBySid(Guid sessionId, Guid? byUserId, string reason, CancellationToken ct = default) |
Revoke a single session by id. Returns true if the session was already revoked (no-op), false if this call performed the revocation. Throws BusinessException(SessionNotFound) if no row exists. |
RevokeAllForUser |
Task<int> RevokeAllForUser(Guid userId, Guid? byUserId, string reason, CancellationToken ct = default) |
Revoke every active session for a user. Returns the number of rows newly revoked. |
RevokeMissionsForAircraft |
Task<int> RevokeMissionsForAircraft(Guid aircraftId, CancellationToken ct = default) |
AZ-533 — auto-revoke every open mission session for an aircraft. Fired on successful /login or /token/refresh from a CompanionPC user. |
GetRevokedSince |
Task<IReadOnlyList<RevokedSession>> GetRevokedSince(DateTime since, CancellationToken ct = default) |
Verifier-poll snapshot. Returns sessions revoked after since whose exp is still in the future (auto-prunes already-expired entries). |
record RevokedSession(Guid Sid, DateTime Exp, DateTime RevokedAt, string? Reason)
Shape returned by GetRevokedSince. Field names match the JSON the /sessions/revoked endpoint serializes to verifiers.
Internal Logic
- Revocation reasons are constants on
SessionRevokedReasons(logged_out,logged_out_all,admin_revoked,post_flight_reconnect,rotated,reuse_detected,family_revoked). - Idempotency —
RevokeBySidreads first, then writes only ifrevoked_at IS NULL. The boolean return signals which side of the race the caller was on. - Mission auto-revoke uses the partial index
sessions_aircraft_active_idx(defined in09_sessions_logout_and_mission.sql) — O(active mission rows for that aircraft). - Snapshot pruning —
GetRevokedSincefiltersexpires_at > now()so the response stays bounded even if revocation history grows large; the endpoint additionally clampssincetonow - 12 hto prevent unbounded historical scans.
Dependencies
IDbFactory— admin connection for updates, read connection for the snapshotSessionentity,SessionRevokedReasons,SessionClassesconstantsBusinessException/ExceptionEnum.SessionNotFound
Consumers
Program.cs/logout→RevokeBySidProgram.cs/logout/all→RevokeAllForUserProgram.cs/sessions/{sid}/revoke(admin-only) →RevokeBySidProgram.cs/sessions/revoked(verifier-poll, gated byrevocationReaderPolicy) →GetRevokedSinceProgram.cs/loginand/token/refresh(when caller isRoleEnum.CompanionPC) →RevokeMissionsForAircraft
Data Models
Operates on the Session entity via AzaionDb.Sessions table.
Configuration
None directly. The /sessions/revoked endpoint hard-codes the 12-hour since floor; review if mission TTL is ever raised above 12 h.
External Integrations
PostgreSQL via IDbFactory.
Security
- The verifier-poll endpoint is gated by
revocationReaderPolicy(ServiceorApiAdminrole). Each verifier deployment (satellite-provider, gps-denied, ui) provisions oneRole=Serviceuser. - The
Cache-Control: no-cacheheader on/sessions/revokedprevents intermediaries from staleing the denylist. - The
revoked_by_user_idcolumn gives an audit trail of "who revoked this session" for admin and user-initiated revocations; system revocations (rotation, reuse, post-flight) leave it null on purpose.
Tests
e2e/Azaion.E2E/Tests/LogoutTests.cs— covers AC-1 (logout revokes session), AC-2 (logout/all), AC-3 (admin revoke), AC-4 (snapshot recent + prune expired), AC-5 (idempotent logout).e2e/Azaion.E2E/Tests/MissionTokenTests.cs— exercisesRevokeMissionsForAircraftvia AC-4 (auto-revoke on reconnect).