Remove references to Managing.Aspire.AppHost and Managing.Aspire.ServiceDefaults from solution and project files; update API project to eliminate unused references and adjust JWT token handling in Program.cs; enhance NSwag generation for Axios and Fetch clients, including new import handling.

This commit is contained in:
2025-12-14 00:18:02 +07:00
parent 0126377486
commit 2157d1f2c9
5 changed files with 72 additions and 49 deletions

View File

@@ -39,7 +39,6 @@
<ItemGroup>
<ProjectReference Include="..\Managing.Bootstrap\Managing.Bootstrap.csproj"/>
<ProjectReference Include="..\Managing.Aspire.ServiceDefaults\Managing.Aspire.ServiceDefaults.csproj"/>
<ProjectReference Include="..\Managing.Core\Managing.Core.csproj"/>
</ItemGroup>

View File

@@ -70,8 +70,6 @@ SentrySdk.Init(options =>
options.Environment = builder.Environment.EnvironmentName;
});
// Add Service Defaults - using extension methods directly
builder.Services.AddServiceDiscovery();
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), ["api"]);
@@ -167,7 +165,9 @@ builder.Host.UseSerilog((hostBuilder, loggerConfiguration) =>
};
loggerConfiguration
.MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", LogEventLevel.Warning) // Filter out EF Core SQL query logs
.MinimumLevel
.Override("Microsoft.EntityFrameworkCore.Database.Command",
LogEventLevel.Warning) // Filter out EF Core SQL query logs
.WriteTo.Console()
.WriteTo.Elasticsearch(es);
});
@@ -225,9 +225,9 @@ var validAudiences = builder.Configuration.GetSection("Authentication:Schemes:Be
.Get<string[]>() ?? Array.Empty<string>();
// Determine if validation should be enabled (enable in production, allow override via config)
var enableIssuerValidation = builder.Configuration.GetValue<bool>("Jwt:ValidateIssuer",
var enableIssuerValidation = builder.Configuration.GetValue<bool>("Jwt:ValidateIssuer",
!builder.Environment.IsDevelopment());
var enableAudienceValidation = builder.Configuration.GetValue<bool>("Jwt:ValidateAudience",
var enableAudienceValidation = builder.Configuration.GetValue<bool>("Jwt:ValidateAudience",
!builder.Environment.IsDevelopment());
// Configure clock skew (tolerance for time differences between servers)
@@ -268,7 +268,7 @@ builder.Services
context.Token = null;
return Task.CompletedTask;
}
// Handle tokens sent without "Bearer " prefix for authenticated endpoints
// The standard middleware expects "Bearer <token>" but some clients send just the token
if (string.IsNullOrEmpty(context.Token))
@@ -284,7 +284,7 @@ builder.Services
// Otherwise, let the default middleware extract it (it will strip "Bearer " automatically)
}
}
// If you want to get the token from a custom header or query string
// var accessToken = context.Request.Query["access_token"];
// if (!string.IsNullOrEmpty(accessToken) &&
@@ -298,30 +298,30 @@ builder.Services
{
var logger = context.HttpContext.RequestServices
.GetService<ILogger<Program>>();
// Check if the endpoint allows anonymous access
var endpoint = context.HttpContext.GetEndpoint();
var allowAnonymous = endpoint?.Metadata.GetMetadata<IAllowAnonymous>() != null;
// For anonymous endpoints with malformed tokens, skip authentication instead of failing
if (allowAnonymous && context.Exception is SecurityTokenMalformedException)
{
logger?.LogDebug("Skipping malformed token validation for anonymous endpoint: {Path}",
logger?.LogDebug("Skipping malformed token validation for anonymous endpoint: {Path}",
context.Request.Path);
context.NoResult(); // Skip authentication, don't fail
return Task.CompletedTask;
}
if (context.Exception is SecurityTokenExpiredException)
{
context.Response.Headers["Token-Expired"] = "true";
logger?.LogWarning("JWT token expired for request: {Path}",
logger?.LogWarning("JWT token expired for request: {Path}",
context.Request.Path);
}
else
{
logger?.LogError(context.Exception,
"JWT authentication failed for request: {Path}",
logger?.LogError(context.Exception,
"JWT authentication failed for request: {Path}",
context.Request.Path);
}
@@ -332,12 +332,12 @@ builder.Services
{
var logger = context.HttpContext.RequestServices
.GetService<ILogger<Program>>();
try
{
var userService = context.HttpContext.RequestServices
.GetRequiredService<IUserService>();
// JWT token contains 'address' claim (not NameIdentifier)
var address = context.Principal.FindFirst("address")?.Value;
@@ -354,7 +354,7 @@ builder.Services
else
{
logger?.LogWarning(
"JWT token validated but user not found for address: {Address}",
"JWT token validated but user not found for address: {Address}",
address);
context.Fail("User not found");
}
@@ -367,7 +367,7 @@ builder.Services
}
catch (Exception ex)
{
logger?.LogError(ex,
logger?.LogError(ex,
"Error during JWT token validation - user lookup failed");
context.Fail("Authentication failed: user lookup error");
}
@@ -482,19 +482,19 @@ app.Use(async (context, next) =>
context.Response.Headers.Append("X-XSS-Protection", "1; mode=block");
context.Response.Headers.Append("Referrer-Policy", "strict-origin-when-cross-origin");
context.Response.Headers.Append("Permissions-Policy", "geolocation=(), microphone=(), camera=()");
// Content Security Policy - only for non-Swagger endpoints
if (!context.Request.Path.StartsWithSegments("/swagger") &&
if (!context.Request.Path.StartsWithSegments("/swagger") &&
!context.Request.Path.StartsWithSegments("/health") &&
!context.Request.Path.StartsWithSegments("/alive"))
{
context.Response.Headers.Append("Content-Security-Policy",
context.Response.Headers.Append("Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;");
}
// Remove server header (optional - Kestrel can be configured separately)
context.Response.Headers.Remove("Server");
await next();
});