# 05 — Identity & Authorization **Spec source**: `../../../suite/_docs/10_auth.md` (suite-wide JWT model), `../../../suite/_docs/00_roles_permissions.md` (the `FL` permission code). **Implementation status**: ✅ implemented. Single policy `FL` is declared and consumed by every controller. > **NOTE (forward-looking)**: post-rename + post-GPS-Denied-removal. Today's `JwtExtensions.cs` also declares a `"GPS"` policy reserved for the (now-removed-from-this-repo) GPS-Denied endpoints. After Jira AZ-EPIC child B7 lands, only `"FL"` remains. **Files**: `Auth/JwtExtensions.cs` ## 1. High-Level Overview **Purpose**: Validate JWT bearer tokens issued by the remote `admin` service and expose the named authorization policy (`FL`) used by controllers in the feature components. This service does not issue tokens -- it consumes them. **Architectural pattern**: ASP.NET Core extension method (`AddJwtAuth`) configuring `IServiceCollection` at DI time. **Upstream dependencies**: None internally. **Downstream consumers**: `07_host` (calls `AddJwtAuth(jwtSecret)` once); `01_vehicle_catalog`, `02_mission_planning` (controllers carry `[Authorize(Policy = "FL")]`). ## 2. Internal Interface ```csharp public static IServiceCollection AddJwtAuth(this IServiceCollection services, string jwtSecret); ``` Side effects: registers `JwtBearerDefaults.AuthenticationScheme` and one named authorization policy in DI: | Policy | Requirement | |--------|-------------| | `"FL"` | JWT contains a `permissions` claim with value `"FL"` | ## 3. Suite-wide JWT pattern This is the canonical "every backend service" identity model in the Azaion suite. Per `../../../suite/_docs/00_top_level_architecture.md` and `../../../suite/_docs/10_auth.md`: ``` ┌─────────────────────┐ ┌──────────────────────┐ │ Operator UI │ POST /login │ admin (.NET, remote) │ │ (React, edge) │ ──────────────► │ central user DB │ │ │ ◄────────────── │ mints HS256 JWT │ │ │ Bearer JWT │ (claim: permissions)│ └──────────┬──────────┘ └──────────────────────┘ │ Bearer JWT (the SAME token reused for every service) │ ├──────────────────► annotations (.NET, edge) -- ANN claim ├──────────────────► missions (.NET, edge) -- FL claim ◄── this service ├──────────────────► satellite-provider (.NET, remote) -- ADM claim └──────────────────► (any future .NET service) ``` Every service (admin, annotations, missions, 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. **This service neither issues tokens nor talks to the central user DB** -- it only validates. The `permissions` claim drives per-service `[Authorize(Policy = "...")]` checks. The role -> permission matrix lives in `../../../suite/_docs/00_roles_permissions.md`. All routes here require `FL`. ## 4. External API None directly. Auth contract is observable only via `401 Unauthorized` / `403 Forbidden` on protected routes. ## 5. Data Access Patterns None. ## 6. Implementation Details **Algorithm**: HMAC-SHA256 signature validation via `SymmetricSecurityKey(UTF-8(jwtSecret))`. Matches the suite-wide shared-secret model. **Token validation flags**: - `ValidateIssuerSigningKey = true` - `ValidateLifetime = true` (with `ClockSkew = 1 minute` -- tighter than .NET's 5-minute default) - `ValidateIssuer = false`, `ValidateAudience = false` -- `iss` / `aud` NOT enforced (consistent with shared-secret intra-suite model). Per the CMMC L2 scorecard (`../../../suite/_docs/05_security/cmmc_l2_scorecard.md` row 3), this is a known finding tracked at the suite level under AZ-487/AZ-494; the remediation will copy the `satellite-provider` pattern across `annotations` and `missions`. **Key Dependencies**: | Library | Version | Purpose | |---------|---------|---------| | `Microsoft.AspNetCore.Authentication.JwtBearer` | 10.0.5 | JWT bearer middleware + handler | | `Microsoft.IdentityModel.Tokens` | (transitive) | `SymmetricSecurityKey`, `TokenValidationParameters` | ## 7. Extensions and Helpers None. ## 8. Caveats & Edge Cases 1. **Shared-secret trust model** -- any service that knows `JWT_SECRET` can mint tokens this API will accept. Not safe for multi-tenant or third-party token issuance. Consistent with the rest of the suite; tightening this is suite-wide work, not a per-service decision. 2. **No claim type for "user id" is consumed** -- only the `permissions` claim is checked. Services don't know who is calling them; per-user audit trails / business rules cannot be enforced at the service layer today. When a future feature needs an "applied by" attribution this gap will need to close. 3. **No offline-grace-window logic in this service** -- `../../../suite/_docs/10_auth.md` describes an offline JWT cache; that lives in the UI / `admin` consumption pattern, not here. 4. **Hardcoded fallback secret** in `Program.cs` (`"development-secret-key-min-32-chars!!"`) is dev-only. Production deployments MUST set `JWT_SECRET`. 5. **`FL` permission code carries the legacy "Flight" name even after the service rename to `missions`.** The plan documents this explicitly: changing the permission code is a fleet-wide auth change (would break every issued token until new ones are minted) and is **NOT** in this Epic's scope. Tracked as a TODO in `../../../suite/_docs/00_roles_permissions.md`. ## 9. Dependency Graph **Must be implemented after**: nothing. **Can be implemented in parallel with**: `04_persistence`, `06_http_conventions`. **Blocks**: `07_host`, `01_vehicle_catalog`, `02_mission_planning`. ## 10. Logging Strategy ASP.NET Core's JwtBearer handler logs token validation outcomes at default levels (Information / Debug). Not customized.