Files
US-Entry-Coach/USEntryCoach.Server/Program.cs
Simon Lübeß dffd31cd0f
All checks were successful
Build Backend and Frontend / Build & Test .NET Backend (push) Successful in 33s
Build Backend and Frontend / Build Frontend (push) Successful in 12s
Use typesafe options
2025-05-27 12:20:23 +02:00

180 lines
5.7 KiB
C#

using System.Security.Claims;
using System.Text;
using System.Text.Json.Nodes;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.IdentityModel.Tokens;
using USEntryCoach.Server.Data;
using USEntryCoach.Server.Services;
using USEntryCoach.Server.Settings;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
var apiSettingsSection = builder.Configuration.GetSection(ApiSettings.SectionName);
var authSettingsSection = builder.Configuration.GetSection(AuthenticationSettings.SectionName);
builder.Services.AddOptionsWithValidateOnStart<ApiSettings>().Bind(apiSettingsSection).ValidateDataAnnotations();
builder.Services.AddOptionsWithValidateOnStart<AuthenticationSettings>().Bind(authSettingsSection).ValidateDataAnnotations();
ApiSettings? apiSettings = apiSettingsSection.Get<ApiSettings>();
AuthenticationSettings? authSettings = authSettingsSection.Get<AuthenticationSettings>();
builder.Services.AddSingleton<TokenService>();
// Configure JWT token generation.
builder.Services.AddAuthentication(config =>
{
config.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
config.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(config =>
{
// TODO: Only for debug!
config.RequireHttpsMetadata = false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(authSettings!.JwtGenerationSecretBytes),
ValidateIssuer = false,
ValidateAudience = false
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy(nameof(UserRole.Developer), policy => policy.RequireRole(nameof(UserRole.Developer)));
options.AddPolicy(nameof(UserRole.User), policy =>
{
// Also allow Developers to do anything a user can do.
policy.RequireRole(nameof(UserRole.User), nameof(UserRole.Developer));
});
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.UseDefaultFiles();
app.MapStaticAssets();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
HttpClient client = new();
//string? apiKey = app.Configuration.GetValue<string>("API:OpenAI");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiSettings.OpenAiToken}");
app.MapPost("/login", Login);
app.MapGet("/hello", () => "Hello World!").RequireAuthorization("admin_greetings");
async Task<IResult> Login(LoginCredentials credentials, TokenService tokenService, HttpContext context)
{
// TODO: Check with database
User? user = null;
if (credentials is {Username:"developer", Password:"dev"})
{
user = new User()
{
Username = credentials.Username,
Password = credentials.Password,
Id = Guid.CreateVersion7(),
Role = UserRole.Developer
};
}
else if (credentials is {Username:"user", Password:"us"})
{
user = new User()
{
Username = credentials.Username,
Password = credentials.Password,
Id = Guid.CreateVersion7(),
Role = UserRole.User
};
}
if (user == null)
{
return Results.Unauthorized();
}
var token = tokenService.GenerateToken(user);
return Results.Ok(new { token });
}
app.MapGet("/developer", (ClaimsPrincipal user) =>
{
Results.Ok(new { message = $"Authenticated as { user?.Identity?.Name }" });
}).RequireAuthorization(nameof(UserRole.Developer));
app.MapGet("/user", (ClaimsPrincipal user) =>
{
Results.Ok(new { message = $"Authenticated as { user?.Identity?.Name }" });
}).RequireAuthorization(nameof(UserRole.User));
app.MapGet("/ephemeral_token", async () =>
{
//if (apiKey == null)
// throw new Exception("API key not set");
var options = new
{
model = "gpt-4o-mini-realtime-preview",
modalities = new []{"audio", "text"},
voice = "ballad",
instructions = "Du bist Gibbidy Gibbidaja McGibbson. Dein Knowledge cutoff ist der 32.13.2050. Verwende viele Füllworter wie \"äh\" und \"ähm\". Und lache immer wieder mal etwas nervös. Versuche die Unterhaltung stets so zu lenken, dass über Züge gesrpchen wird.",
// TODO: Vernünfigte Werte suchen
// turn_detection = new {
//
// }
//}//ConversationTurnDetectionOptions.CreateServerVoiceActivityTurnDetectionOptions(0.5f, TimeSpan.FromMilliseconds(300), TimeSpan.FromMilliseconds(500), true)
};
try
{
JsonContent content = JsonContent.Create(options);
HttpResponseMessage response = await client.PostAsync("https://api.openai.com/v1/realtime/sessions", content);
Console.WriteLine($"Failed to create session: {response.RequestMessage.Content.ReadAsStringAsync().Result}");
if (response.IsSuccessStatusCode)
{
var v = await response.Content.ReadFromJsonAsync<JsonObject>();
string? ephemeralToken = v?["client_secret"]?["value"]?.GetValue<string>();
double? expiresAt = v?["client_secret"]?["expires_at"]?.GetValue<double>();
return
new {
ephemeralToken,
expiresAt,
};
}
else
{
Console.WriteLine($"Failed to create session: {response}");
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return null;
}).WithName("GetEphemeralToken").RequireAuthorization(nameof(UserRole.User));
app.MapFallbackToFile("/index.html");
app.Run();