- Deleted the `POST /resources/get/{dataFolder?}` and `GET /resources/get-installer` endpoints as part of the architectural shift towards simplified resource management.
- Removed associated methods and configurations, including `ResourcesService.GetEncryptedResource`, `ResourcesService.GetInstaller`, and related properties in `ResourcesConfig`.
- Cleaned up environment variables and configuration files to reflect the removal of installer-related settings.
- Eliminated the `GetResourceRequest` DTO and its validator, along with the `WrongResourceName` error code.
- Updated documentation to clarify the changes in resource handling and the retirement of per-user file encryption.
Co-authored-by: Cursor <cursoragent@cursor.com>
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
sessionstable withclass='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/MissionSessionResponseDTOs inAzaion.Common/Requests/.- Validation:
planned_duration_h∈ [0.1, 12];mission_idmatchesM-YYYY-MM-DD-NNN;aircraft_idexists in users table withRole=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 /
cnfclaim) — 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_idclaims — 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_regionbbox is informational until satellite-provider enforces it. Document the planned enforcement in the cross-workspace coordination note.