using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; namespace SatelliteProvider.Api.Authentication; public static class AuthenticationServiceCollectionExtensions { public const string JwtSecretEnvVar = "JWT_SECRET"; public const string JwtSecretConfigKey = "Jwt:Secret"; public const int MinSecretByteLength = 32; public static IServiceCollection AddSatelliteJwt(this IServiceCollection services, IConfiguration configuration) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configuration); var secret = ResolveSecretOrThrow(configuration); var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); services .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(30), ValidateIssuer = false, ValidateAudience = false, RequireSignedTokens = true, RequireExpirationTime = true }; }); return services; } internal static string ResolveSecretOrThrow(IConfiguration configuration) { var secret = Environment.GetEnvironmentVariable(JwtSecretEnvVar); if (string.IsNullOrWhiteSpace(secret)) { secret = configuration[JwtSecretConfigKey]; } if (string.IsNullOrWhiteSpace(secret)) { throw new InvalidOperationException( $"JWT secret is not configured. Set the {JwtSecretEnvVar} environment variable " + $"or the {JwtSecretConfigKey} configuration key to a value of at least {MinSecretByteLength} bytes."); } var byteLength = Encoding.UTF8.GetByteCount(secret); if (byteLength < MinSecretByteLength) { throw new InvalidOperationException( $"JWT secret is too short ({byteLength} bytes). HMAC-SHA256 requires at least {MinSecretByteLength} bytes " + $"per RFC 2104 ยง3. Set {JwtSecretEnvVar} or {JwtSecretConfigKey} to a longer value."); } return secret; } }