Files
admin/_docs/02_tasks/done/AZ-533_mission_token_uav.md
Oleksandr Bezdieniezhnykh 8e7c602f51
ci/woodpecker/push/01-test Pipeline failed
ci/woodpecker/push/02-build-push unknown status
[AZ-535] [AZ-533] Logout/revocation surface + UAV mission tokens
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>
2026-05-14 05:51:23 +03:00

5.3 KiB

Mission-Token Issuance for Disconnected UAV Operations

Task: AZ-533_mission_token_uav Name: Mission-token issuance for disconnected UAV operations Description: New POST /sessions/mission endpoint that issues a single long-lived (≤11 h) access token for one specific flight. Narrowly scoped (mission_id, aircraft_id, aud), one-shot, auto-revoked on aircraft reconnect. Solves the "10 h offline UAV vs 15 min ground access token" tension without weakening interactive-session security. Complexity: 5 points Dependencies: AZ-531 (needs sessions table for revocation tracking). Can implement in parallel; final wiring depends on AZ-531. Component: Admin API + Services + DataAccess Tracker: AZ-533 Epic: AZ-529

Problem

UAV missions can fly up to 10 h fully offline (no Starlink, no admin reachability). Standard short-lived access tokens (15 min) plus refresh-on-network are physically impossible during flight. Today's solution would be "set JWT lifetime to 4 h and pray", which is both too short for full missions and too long for ground operations — a single lifetime can't satisfy both.

Outcome

  • New endpoint POST /sessions/mission (auth: existing interactive access token, MFA proven within last 15 min by virtue of refresh chain).
  • Body: { mission_id, aircraft_id, planned_duration_h, requested_scope }.
  • Returns: a single long-lived access token (no refresh) with custom claims:
{
  "sub": "<pilot-or-aircraft-user-id>",
  "iss": "AzaionApi",
  "aud": "satellite-provider",
  "exp": "now + planned_duration_h + 1h",
  "mission_id": "M-2026-05-14-042",
  "aircraft_id": "UAV-117",
  "valid_region": { "...bbox..." : "..." },
  "permissions": ["GPS"],
  "sid": "<session-id>",
  "jti": "<token-id>",
  "token_class": "mission"
}
  • Mission tokens are recorded in sessions table with class='mission' so logout/revocation works.
  • On post-flight reconnect (any successful auth call from the same aircraft_id), all open mission sessions for that aircraft are auto-revoked.

Scope

Included

  • MissionSessionRequest / MissionSessionResponse DTOs in Azaion.Common/Requests/.
  • Validation: planned_duration_h ∈ [0.1, 12]; mission_id matches M-YYYY-MM-DD-NNN; aircraft_id exists in users table with Role=CompanionPC.
  • Auto-revoke-on-reconnect logic in middleware (cheap: index on sessions(aircraft_id, class, revoked_at)).
  • Tests: happy path, scope-narrowing, max-duration cap, auto-revoke on next call.

Excluded

  • Hardware binding (mTLS / DPoP / cnf claim) — separate future ticket. This ticket gets the lifetime + scope right; hardware binding is a hardening pass.
  • Verifier-side enforcement of mission_id/valid_region/aircraft_id claims — filed under satellite-provider once admin ships.
  • Pre-flight ground station UX (file/load mission token onto UAV) — client/UI concern.

Acceptance Criteria

AC-1: Mission token issued with correct lifetime Given an authenticated pilot session and planned_duration_h=9 When POST /sessions/mission is called Then response includes a single access token with exp ≈ now + 10h (±60s), no refresh token, token_class="mission".

AC-2: Hard cap enforced Given planned_duration_h=15 When called Then 400 with detail "planned_duration_h must be ≤ 12".

AC-3: Scope claims present Given a request with mission_id and aircraft_id When the returned token is decoded Then mission_id, aircraft_id, aud="satellite-provider", permissions, sid, jti all present.

AC-4: Auto-revoke on reconnect Given aircraft UAV-117 has an open mission session M-001 When UAV-117 calls any /token/refresh or /login endpoint successfully Then the M-001 mission session is marked revoked_reason='post_flight_reconnect' and that token stops working.

AC-5: Issued only against an authenticated session Given no auth header When POST /sessions/mission is called Then 401.

AC-6: Auth claim chain proven (MFA step-up) Given the requesting access token has amr=["pwd"] only (no MFA) When POST /sessions/mission is called (after AZ-534 ships) Then 403 with detail "mission tokens require step-up MFA". Until AZ-534 ships, AC-6 is enforced as a TODO comment in code; do not block this ticket on AZ-534.

Blackbox Tests

AC Ref Initial Data/Conditions What to Test Expected Behavior NFR References
AC-1 Pilot session, 9h request POST /sessions/mission exp ≈ now+10h, no refresh, class=mission
AC-2 15h request POST /sessions/mission 400 with cap message
AC-3 Mission token from AC-1 Decode claims mission_id, aircraft_id, aud, sid, jti present
AC-4 Open mission for UAV-117 UAV-117 calls /token/refresh Mission revoked, token dead
AC-5 No auth header POST /sessions/mission 401
AC-6 amr=["pwd"] token (post-AZ-534) POST /sessions/mission 403 step-up required

Risks / Notes

  • Long-lived tokens are dangerous if leaked. Hardware binding is the right long-term answer; document this as known-risk in _docs/05_security/security_report.md.
  • The valid_region bbox is informational until satellite-provider enforces it. Document the planned enforcement in the cross-workspace coordination note.