# Module: Azaion.Services.MissionTokenService ## Purpose Issues long-lived (≤ 12 h) single-use access tokens for offline UAV missions. Distinct from `AuthService.CreateToken` because: - Lifetime is per-mission (`planned_duration_h + 1 h` buffer), not the 15-minute interactive policy. - Audience is narrowed to `satellite-provider`, not the broad admin audience. - No refresh: a single token covers the entire flight, then dies. - Carries mission-specific claims (`mission_id`, `aircraft_id`, `valid_region`, `permissions`). > Added in cycle 2 (2026-05-14) by AZ-533 (Epic AZ-529). Solves the "10 h offline UAV vs. 15 min interactive access token" tension without weakening interactive-session security. ## Public Interface ### IMissionTokenService | Method | Signature | Description | |--------|-----------|-------------| | `Issue` | `Task Issue(Guid pilotUserId, MissionSessionRequest request, CancellationToken ct = default)` | Validates the request, persists a `class='mission'` row in `sessions`, mints an ES256 access token bound to that session id, returns the token + expiry + session id. | ## Internal Logic - **Validation**: - `mission_id` must match `^M-\d{4}-\d{2}-\d{2}-\d{3}$` (compiled regex). - `planned_duration_h` ∈ `[0.1, 12.0]` — anything outside throws `BusinessException(InvalidMissionRequest)` (HTTP 400). - `aircraft_id` must exist in `users` with `Role=CompanionPC`; otherwise `BusinessException(AircraftNotFound)`. - **Session row first, then token** — the row is inserted *before* the JWT is minted so revocation lookups can never miss a token already in the wild. - **Lifetime** = `planned_duration_h + 1.0` (the 1-hour buffer covers post-flight reconnect grace). - **Family handling** — mission sessions are their own family (`family_id = id`); they never rotate. - **Token claims**: `sub` = pilotUserId, `sid` = session id, `jti` = unique token id, `mission_id`, `aircraft_id`, `token_class="mission"`, optional `permissions` (multi-valued), optional `valid_region` (JSON-typed claim). Audience pinned to `satellite-provider`. - **Auto-revoke on reconnect** is implemented in `Program.cs` via `ISessionService.RevokeMissionsForAircraft`, fired from `/login` and `/token/refresh` whenever the caller is a `CompanionPC` user. ## Dependencies - `IDbFactory` — admin connection for inserting the mission session row, read connection for the aircraft existence check - `IJwtSigningKeyProvider` — ES256 active key - `IOptions` — issuer - `Session` entity, `SessionClasses.Mission` constant - `MissionSessionRequest` / `MissionSessionResponse` DTOs ## Consumers - `Program.cs` `/sessions/mission` (requires interactive auth; per AZ-533 the AC-6 step-up MFA gate is a TODO until org-wide MFA adoption) ## Data Models Operates on the `Session` entity (`class='mission'`, `aircraft_id` set, `refresh_hash` null). ## Configuration - `JwtConfig.Issuer` — issuer claim of the minted token. - Hard-coded constants: - `MissionAudience = "satellite-provider"` — verifier-side audience gate. - `MaxDurationHours = 12.0`, `MinDurationHours = 0.1`. - `LifetimeBufferHours = 1.0`. ## External Integrations PostgreSQL via `IDbFactory`. Token consumed by the `satellite-provider` workspace (verifier-side enforcement of `mission_id`/`aircraft_id`/`valid_region` is filed under that workspace). ## Security - Long-lived tokens are inherently dangerous if leaked. Hardware binding (mTLS / DPoP / `cnf`) is the long-term answer; documented as a known risk in `_docs/05_security/security_report.md`. - The narrowed audience (`satellite-provider`) prevents a stolen mission token from being usable against the admin API itself; admin endpoints still require `JwtConfig.Audience`. - The `valid_region` bbox is informational until `satellite-provider` enforces it (cross-workspace coordination ticket). - Mission tokens are auto-revoked the moment the aircraft reconnects (`/login` or `/token/refresh` from a `CompanionPC` user). Verifiers polling `/sessions/revoked` see the revocation within their poll interval (≤ 30 s). ## Tests - `e2e/Azaion.E2E/Tests/MissionTokenTests.cs` — AC-1 (correct lifetime + claims), AC-2 (12h cap), AC-3 (scope claims), AC-4 (auto-revoke on reconnect), AC-5 (auth required).