Files
SlipItIn/SlipItIn.Server/Program.cs
Tim Krampitz 540cda102f Support Android emulator API access
Enable CORS in all environments and disable HTTPS redirect in dev.
Seed the database after migrations.
Make the API base URL configurable, defaulting to the Android
emulator host, and use per-config Android manifests.
2026-08-22 19:05:53 +02:00

130 lines
4.0 KiB
C#

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using SlipItIn.Server.Data;
using SlipItIn.Server.Hubs;
using SlipItIn.Server.Services;
using System.Security.Claims;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Aspire Service Defaults (OpenTelemetry, Health Checks, Service Discovery)
builder.AddServiceDefaults();
builder.AddNpgsqlDataSource(connectionName: "postgresdb");
builder.Services.AddDbContextFactory<SlipItInDbContext>(options => options.UseNpgsql());
// Services registrieren
builder.Services.AddTransient<IGameService, GameService>();
// JWT Authentication
var jwtKey = builder.Configuration["Jwt:Key"] ?? "YourSuperSecretKeyThatIsAtLeast32CharactersLong!";
var jwtIssuer = builder.Configuration["Jwt:Issuer"] ?? "SlipItInServer";
var jwtAudience = builder.Configuration["Jwt:Audience"] ?? "SlipItInClient";
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtIssuer,
ValidAudience = jwtAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
};
// SignalR-Support für JWT in Query String
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
},
OnTokenValidated = async context =>
{
var userIdClaim = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (!int.TryParse(userIdClaim, out var userId))
{
context.Fail("Invalid user claim.");
return;
}
var dbFactory = context.HttpContext.RequestServices.GetRequiredService<IDbContextFactory<SlipItInDbContext>>();
await using var db = await dbFactory.CreateDbContextAsync();
var userExists = await db.Users.AnyAsync(u => u.Id == userId && u.IsActive);
if (!userExists)
{
context.Fail("User no longer exists or is inactive.");
}
}
};
});
builder.Services.AddAuthorization();
// SignalR
builder.Services.AddSignalR();
// CORS für Development
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
// Controller & OpenAPI
builder.Services.AddControllers();
builder.Services.AddOpenApi();
var app = builder.Build();
// Aspire Default Endpoints (Health Checks)
app.MapDefaultEndpoints();
// Migrations anwenden und Testdaten seeden (nur wenn die Datenbank leer ist)
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SlipItInDbContext>();
db.Database.Migrate();
await DbSeeder.SeedAsync(db);
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseCors("AllowAll");
// Beim Emulator/geräteübergreifenden Zugriff würde die HTTPS-Umleitung
// den Client auf eine nicht vertrauenswürdige Dev-Zertifikats-URL schicken.
if (!app.Environment.IsDevelopment())
{
app.UseHttpsRedirection();
}
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHub<GameHub>("/hubs/game");
app.Run();