# Module: `Azaion.Missions.Auth` **Files (1)**: `Auth/JwtExtensions.cs` > **NOTE (forward-looking)**: this module's source paths and namespace will become `Azaion.Missions.*` after the `flights -> missions` rename ticket lands (Jira AZ-EPIC, child B5 / B7). Today the file still says `Azaion.Flights`. The behavior described below already matches the post-rename intent: only the `FL` policy remains, the `GPS` policy is removed (per B7). ## Purpose Single static extension (`AddJwtAuth`) that registers JWT bearer authentication and the named authorization policy `FL` used by controllers. ## Public Interface ```csharp public static class JwtExtensions { public static IServiceCollection AddJwtAuth(this IServiceCollection services, string jwtSecret); } ``` ## Internal Logic 1. `AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(...)` configures token validation: - `IssuerSigningKey = SymmetricSecurityKey(UTF-8(jwtSecret))` -> **HS256 / shared-secret** validation. - `ValidateIssuer = false`, `ValidateAudience = false` -- `iss` / `aud` claims are NOT checked. Tokens with any issuer/audience are accepted as long as the signature and lifetime are valid. (CMMC L2 finding -- see `../../suite/_docs/05_security/cmmc_l2_scorecard.md` row 3 and the suite-level remediation tracked under AZ-487/AZ-494.) - `ValidateIssuerSigningKey = true`, `ValidateLifetime = true`. - `ClockSkew = 1 minute` (tighter than the .NET default of 5 minutes). 2. `AddAuthorizationBuilder()` registers one policy: - `"FL"` -> requires the JWT to contain a `permissions` claim with value `"FL"`. `RequireClaim("permissions", "FL")` matches on a claim named `"permissions"` whose value equals `"FL"`. With multi-permission tokens, the token typically has multiple `permissions` claims, one per permission. ## Suite-wide JWT pattern This service consumes JWTs minted by the remote `admin` service against the central user PostgreSQL (per `../../suite/_docs/00_top_level_architecture.md` and `../../suite/_docs/10_auth.md`). Every `.NET` service in the suite -- `admin`, `annotations`, `missions` (this one), `satellite-provider` -- shares one HMAC secret (`JWT_SECRET`) and validates tokens locally with no network round-trip. The user logs in once at the UI; the resulting bearer token is reusable across every service. ## Dependencies - `Microsoft.AspNetCore.Authentication.JwtBearer` (NuGet, pinned to `10.0.5`) - `Microsoft.IdentityModel.Tokens` (transitive -- `SymmetricSecurityKey`, `TokenValidationParameters`) - `System.Text` (for `Encoding.UTF8`) No internal dependencies. ## Consumers - `Program.cs` -- `builder.Services.AddJwtAuth(jwtSecret)` is called once at startup. - Controllers reference the policy indirectly via `[Authorize(Policy = "FL")]` (used on both `VehiclesController` and `MissionsController`). ## Configuration Reads no configuration directly -- `jwtSecret` is passed by the caller. `Program.cs` resolves it from `IConfiguration["JWT_SECRET"]` -> `Environment.GetEnvironmentVariable("JWT_SECRET")` -> fallback `"development-secret-key-min-32-chars!!"`. ## External Integrations None at the network level -- token validation is purely cryptographic against the shared secret. ## Security - **Algorithm**: HMAC-SHA256 via `SymmetricSecurityKey`. The token issuer (`admin`) must use the SAME secret to sign -- there is no public-key flow. - **No issuer/audience validation** -- any service that knows the shared secret can mint tokens that this API will accept. This trust model assumes the secret is private to the suite; it is not safe for multi-tenant or third-party token issuance. - **Clock skew tolerance**: 1 minute (tight, intentional). - The fallback secret in `Program.cs` is hardcoded. It MUST be overridden in production. ## Tests None present. ## Notes / Smells 1. **Single permission (`FL`) gates the whole API.** All routes carry `[Authorize(Policy = "FL")]`. There is no operator-vs-admin distinction at this layer; granular permissions are governed by the role->permission matrix in `../../suite/_docs/00_roles_permissions.md`. 2. **No authentication scheme name override** -- uses `JwtBearerDefaults.AuthenticationScheme` ("Bearer"). Consistent. 3. **No claim type for "user id"** -- only the `permissions` claim is consumed; whatever subject identity the issuer puts in the token is ignored at the policy layer. Audit logs / business rules that need a user identifier currently have no per-call user binding (services don't take `HttpContext.User`). When `02_mission_planning` adds attribution to actions like waypoint-set / mission-rename, this becomes a blocker.