Files
satellite-provider/SatelliteProvider.Api/Authentication/AuthenticationServiceCollectionExtensions.cs
T
Oleksandr Bezdieniezhnykh 275ee1b554
ci/woodpecker/push/01-test Pipeline failed
ci/woodpecker/push/02-build-push unknown status
Add TileProvision configuration and gRPC service for tile delivery
- Introduced new TileProvision settings in appsettings.json, including MaxTilesPerBatch and ProgressEmitIntervalSeconds.
- Configured TileProvisionConfig in Program.cs to bind the new settings.
- Added gRPC service for RouteTileDelivery in Program.cs to handle tile delivery requests.
- Updated SatelliteProvider.Api.csproj to include Grpc.AspNetCore package and added protobuf file for tile provision.
- Enhanced AuthenticationServiceCollectionExtensions to handle JWT token extraction from the Authorization header.
- Registered additional services in RouteManagementServiceCollectionExtensions for tile processing.

These changes enhance the API's capability to manage tile provisioning and delivery efficiently.
2026-06-23 13:18:59 +03:00

111 lines
4.5 KiB
C#

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 string JwtIssuerEnvVar = "JWT_ISSUER";
public const string JwtIssuerConfigKey = "Jwt:Issuer";
public const string JwtAudienceEnvVar = "JWT_AUDIENCE";
public const string JwtAudienceConfigKey = "Jwt:Audience";
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 issuer = ResolveRequiredOrThrow(configuration, JwtIssuerEnvVar, JwtIssuerConfigKey, "JWT issuer");
var audience = ResolveRequiredOrThrow(configuration, JwtAudienceEnvVar, JwtAudienceConfigKey, "JWT audience");
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 = true,
ValidIssuer = issuer,
ValidateAudience = true,
ValidAudience = audience,
RequireSignedTokens = true,
RequireExpirationTime = true
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var authHeader = context.Request.Headers.Authorization.FirstOrDefault();
if (!string.IsNullOrEmpty(authHeader)
&& authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
context.Token = authHeader["Bearer ".Length..].Trim();
}
return Task.CompletedTask;
},
};
});
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;
}
// AZ-494: required non-secret config (iss / aud). Fail-fast contract mirrors
// JWT_SECRET — missing or whitespace-only values throw at startup so a
// production deploy without the operator-confirmed values cannot silently
// accept tokens with arbitrary issuer/audience claims.
internal static string ResolveRequiredOrThrow(IConfiguration configuration, string envVar, string configKey, string humanLabel)
{
var value = Environment.GetEnvironmentVariable(envVar);
if (string.IsNullOrWhiteSpace(value))
{
value = configuration[configKey];
}
if (string.IsNullOrWhiteSpace(value))
{
throw new InvalidOperationException(
$"{humanLabel} is not configured. Set the {envVar} environment variable " +
$"or the {configKey} configuration key. (See AZ-494 task spec.)");
}
return value;
}
}