mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 12:21:09 +00:00
8e7c602f51
AZ-535: POST /logout (caller's session), /logout/all (all sessions for user),
admin POST /sessions/{sid}/revoke, and verifier-only GET /sessions/revoked
snapshot. New Service role gates the snapshot. Idempotent revoke; reason +
revoked_by_user_id audited per row.
AZ-533: POST /sessions/mission mints a long-lived no-refresh ES256 token bound
to one aircraft + one mission. Audience narrowed to satellite-provider, hard
12 h cap, persisted as class='mission' so the existing logout/revoke surface
covers it. Successful CompanionPC /login or /token/refresh auto-revokes that
aircraft's open mission session (post-flight reconnect).
Schema: 09_sessions_logout_and_mission.sql adds revoked_by_user_id, class,
aircraft_id; drops NOT NULL on refresh_hash for mission rows; adds two partial
indexes for the auto-revoke and snapshot hot paths.
Tests: 13 new e2e tests, all green; full suite 75/76 (1 pre-existing flake in
PasswordHashingTests AC5 timing assertion, unrelated to this batch).
Co-authored-by: Cursor <cursoragent@cursor.com>
64 lines
2.6 KiB
C#
64 lines
2.6 KiB
C#
namespace Azaion.Common.Entities;
|
|
|
|
/// <summary>
|
|
/// AZ-531 — refresh-token session row. One row per issued refresh token. A
|
|
/// "session family" is the chain of rotated sessions that all share the same
|
|
/// <see cref="FamilyId"/>; reuse-detection keys off it.
|
|
/// </summary>
|
|
public class Session
|
|
{
|
|
public Guid Id { get; set; }
|
|
public Guid UserId { get; set; }
|
|
/// <summary>
|
|
/// AZ-531 — sha256(opaque refresh) for interactive sessions. AZ-533 mission
|
|
/// sessions have no refresh value and store NULL here.
|
|
/// </summary>
|
|
public string? RefreshHash { get; set; }
|
|
public Guid FamilyId { get; set; }
|
|
public DateTime IssuedAt { get; set; }
|
|
public DateTime LastUsedAt { get; set; }
|
|
public DateTime ExpiresAt { get; set; }
|
|
public DateTime? RevokedAt { get; set; }
|
|
public string? RevokedReason { get; set; }
|
|
public Guid? ParentSessionId { get; set; }
|
|
public DateTime FamilyStartedAt { get; set; }
|
|
|
|
/// <summary>
|
|
/// AZ-535 — audit trail for who revoked the session (user id of the admin or
|
|
/// the user themselves on /logout). Null for system revocations (rotation,
|
|
/// reuse detection, post-flight reconnect).
|
|
/// </summary>
|
|
public Guid? RevokedByUserId { get; set; }
|
|
|
|
/// <summary>
|
|
/// AZ-533 — session class. <see cref="SessionClasses.Interactive"/> is the
|
|
/// default refresh-backed interactive session (AZ-531); <see cref="SessionClasses.Mission"/>
|
|
/// is a long-lived no-refresh token issued for a single UAV mission.
|
|
/// </summary>
|
|
public string Class { get; set; } = SessionClasses.Interactive;
|
|
|
|
/// <summary>
|
|
/// AZ-533 — for mission sessions: the aircraft (CompanionPC user) the mission
|
|
/// token belongs to. Used by the auto-revoke-on-reconnect middleware. Null for
|
|
/// interactive sessions.
|
|
/// </summary>
|
|
public Guid? AircraftId { get; set; }
|
|
}
|
|
|
|
public static class SessionRevokedReasons
|
|
{
|
|
public const string Rotated = "rotated";
|
|
public const string ReuseDetected = "reuse_detected";
|
|
public const string LoggedOut = "logged_out";
|
|
public const string LoggedOutAll = "logged_out_all";
|
|
public const string AdminRevoked = "admin_revoked";
|
|
public const string PostFlightReconnect = "post_flight_reconnect";
|
|
public const string FamilyRevoked = "family_revoked";
|
|
}
|
|
|
|
public static class SessionClasses
|
|
{
|
|
public const string Interactive = "interactive";
|
|
public const string Mission = "mission";
|
|
}
|