Updated JWT authentication to use configuration values instead of hardcoded secrets, improving security and flexibility. Enhanced CORS policy to conditionally allow origins based on configuration settings, with logging for permissive defaults. Updated README to reflect project renaming and clarify service context.
4.5 KiB
Module: Azaion.Missions.Auth
Files (1): Auth/JwtExtensions.cs
NOTE (forward-looking): this module's source paths and namespace will become
Azaion.Missions.*after theflights -> missionsrename ticket lands (Jira AZ-EPIC, child B5 / B7). Today the file still saysAzaion.Flights. The behavior described below already matches the post-rename intent: only theFLpolicy remains, theGPSpolicy 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
public static class JwtExtensions {
public static IServiceCollection AddJwtAuth(this IServiceCollection services, string jwtSecret);
}
Internal Logic
AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(...)configures token validation:IssuerSigningKey = SymmetricSecurityKey(UTF-8(jwtSecret))-> HS256 / shared-secret validation.ValidateIssuer = false,ValidateAudience = false--iss/audclaims 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.mdrow 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).
AddAuthorizationBuilder()registers one policy:"FL"-> requires the JWT to contain apermissionsclaim 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 to10.0.5)Microsoft.IdentityModel.Tokens(transitive --SymmetricSecurityKey,TokenValidationParameters)System.Text(forEncoding.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 bothVehiclesControllerandMissionsController).
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.csis hardcoded. It MUST be overridden in production.
Tests
None present.
Notes / Smells
- 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. - No authentication scheme name override -- uses
JwtBearerDefaults.AuthenticationScheme("Bearer"). Consistent. - No claim type for "user id" -- only the
permissionsclaim 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 takeHttpContext.User). When02_mission_planningadds attribution to actions like waypoint-set / mission-rename, this becomes a blocker.