Files
admin/_docs/02_document/modules/services_session_service.md
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

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 RevokeMissionsForAircraft path 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).
  • IdempotencyRevokeBySid reads first, then writes only if revoked_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 in 09_sessions_logout_and_mission.sql) — O(active mission rows for that aircraft).
  • Snapshot pruningGetRevokedSince filters expires_at > now() so the response stays bounded even if revocation history grows large; the endpoint additionally clamps since to now - 12 h to prevent unbounded historical scans.

Dependencies

  • IDbFactory — admin connection for updates, read connection for the snapshot
  • Session entity, SessionRevokedReasons, SessionClasses constants
  • BusinessException / ExceptionEnum.SessionNotFound

Consumers

  • Program.cs /logoutRevokeBySid
  • Program.cs /logout/allRevokeAllForUser
  • Program.cs /sessions/{sid}/revoke (admin-only) → RevokeBySid
  • Program.cs /sessions/revoked (verifier-poll, gated by revocationReaderPolicy) → GetRevokedSince
  • Program.cs /login and /token/refresh (when caller is RoleEnum.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 (Service or ApiAdmin role). Each verifier deployment (satellite-provider, gps-denied, ui) provisions one Role=Service user.
  • The Cache-Control: no-cache header on /sessions/revoked prevents intermediaries from staleing the denylist.
  • The revoked_by_user_id column 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 — exercises RevokeMissionsForAircraft via AC-4 (auto-revoke on reconnect).