Files
mealplanner/backend/Program.cs
Claude 2004883e02 fix: disable JWT inbound claim mapping for Zitadel sub claim
.NET remaps "sub" to a long URI claim type by default, causing
User.FindFirst("sub") to return null. MapInboundClaims=false
preserves the original claim names.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 07:13:17 +00:00

72 lines
2.3 KiB
C#

using Microsoft.EntityFrameworkCore;
using MealPlanner.Data;
using MealPlanner.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers()
.AddJsonOptions(o =>
{
o.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;
o.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
// Zitadel JWT Bearer Auth
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = builder.Configuration["Zitadel:Issuer"] ?? "https://auth.kuns.dev";
options.Audience = builder.Configuration["Zitadel:ClientId"] ?? "";
options.MapInboundClaims = false; // Prevent .NET from remapping "sub" to long URI
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = false, // Zitadel uses client_id as audience in some configs
ValidateLifetime = true,
NameClaimType = "sub",
};
});
builder.Services.AddAuthorization();
builder.Services.AddHttpClient<TheMealDbClient>();
builder.Services.AddScoped<RecipeService>();
builder.Services.AddScoped<MealPlanService>();
builder.Services.AddScoped<ShoppingListService>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var allowedOrigin = builder.Configuration["AllowedOrigin"] ?? "https://essen.kuns.dev";
builder.Services.AddCors(options =>
options.AddDefaultPolicy(policy =>
policy.WithOrigins(allowedOrigin, "http://localhost:5173")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()));
builder.Services.AddHealthChecks();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Database.Migrate();
}
app.UseCors();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");
app.UseStaticFiles();
app.Run();