From 71a0fb48fe6824d99b041aeac3321634ffe1438c Mon Sep 17 00:00:00 2001 From: tactile Date: Tue, 20 Jan 2026 15:51:35 +0500 Subject: [PATCH] Enhance AppConfig with JWT settings and update Program.cs for improved authentication handling. Modify README.md to reflect new environment variable structure for configuration. --- backend-dotnet/AppConfig.cs | 9 +++ .../InsecureDevAuthenticationHandler.cs | 36 ++++++++++ backend-dotnet/Program.cs | 66 +++++++------------ backend-dotnet/README.md | 26 ++++++-- 4 files changed, 87 insertions(+), 50 deletions(-) create mode 100644 backend-dotnet/InsecureDevAuthenticationHandler.cs diff --git a/backend-dotnet/AppConfig.cs b/backend-dotnet/AppConfig.cs index d1da523..a1fec85 100644 --- a/backend-dotnet/AppConfig.cs +++ b/backend-dotnet/AppConfig.cs @@ -12,7 +12,16 @@ public class AppConfig public string? InsecureDevEmail { get; set; } public string[] AllowedOrigins { get; set; } = ["http://localhost:3000", "https://localhost:3000"]; + // JWT configuration + public string NameClaimType { get; set; } = "name"; + public string RoleClaimType { get; set; } = "role"; + public string EmailClaimType { get; set; } = "email"; + public string AuthRequestEmailHeader { get; set; } = "X-Auth-Request-Email"; + public TimeSpan ClockSkew { get; set; } = TimeSpan.FromMinutes(5); + public string ConnectionString => $"Host={DbHost};Port={DbPort};Database={DbName};Username={DbUser};Password={DbPassword}"; } + + diff --git a/backend-dotnet/InsecureDevAuthenticationHandler.cs b/backend-dotnet/InsecureDevAuthenticationHandler.cs new file mode 100644 index 0000000..3c18fcf --- /dev/null +++ b/backend-dotnet/InsecureDevAuthenticationHandler.cs @@ -0,0 +1,36 @@ +using DexDemoBackend; +using Microsoft.AspNetCore.Authentication; +using System.Security.Claims; +using Microsoft.Extensions.Options; +using System.Text.Encodings.Web; + +public class InsecureDevAuthenticationHandler : AuthenticationHandler +{ + private readonly AppConfig _config; + + public InsecureDevAuthenticationHandler( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + AppConfig config) : base(options, logger, encoder) + { + _config = config; + } + + protected override Task HandleAuthenticateAsync() + { + var email = _config.InsecureDevEmail ?? "dev@example.com"; + + var claims = new[] + { + new Claim(ClaimTypes.Name, email), + new Claim(_config.EmailClaimType, email) + }; + + var identity = new ClaimsIdentity(claims, Scheme.Name); + var principal = new ClaimsPrincipal(identity); + var ticket = new AuthenticationTicket(principal, Scheme.Name); + + return Task.FromResult(AuthenticateResult.Success(ticket)); + } +} diff --git a/backend-dotnet/Program.cs b/backend-dotnet/Program.cs index b7ab35d..908356a 100644 --- a/backend-dotnet/Program.cs +++ b/backend-dotnet/Program.cs @@ -1,5 +1,6 @@ using Dapper; using DexDemoBackend; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -8,10 +9,10 @@ using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Tokens; using Npgsql; using System.Security.Claims; +using Microsoft.Extensions.Logging; var builder = WebApplication.CreateBuilder(args); -// Configure AppConfig using IConfiguration builder.Services.Configure(builder.Configuration.GetSection("AppConfig")); builder.Services.AddSingleton(sp => sp.GetRequiredService>().Value); @@ -31,7 +32,6 @@ builder.Services.ConfigureHttpJsonOptions(options => var app = builder.Build(); Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; -// Get current config from DI (supports reload) var currentConfig = app.Services.GetRequiredService(); app.UseCors(policy => @@ -44,7 +44,6 @@ app.UseCors(policy => app.UseAuthentication(); app.UseAuthorization(); -// Global error handling middleware app.Use(async (context, next) => { try @@ -140,6 +139,14 @@ app.Run(); static void ConfigureJwtAuthentication(IServiceCollection services, AppConfig config) { + if (config.InsecureDevMode) + { + services.AddAuthentication("InsecureDev") + .AddScheme("InsecureDev", _ => { }); + + return; + } + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { @@ -152,35 +159,33 @@ static void ConfigureJwtAuthentication(IServiceCollection services, AppConfig co ValidateAudience = false, ValidateLifetime = true, ValidateIssuerSigningKey = true, - ClockSkew = OidcConfigConstants.ClockSkew, - NameClaimType = OidcConfigConstants.NameClaimType, - RoleClaimType = OidcConfigConstants.RoleClaimType + ClockSkew = config.ClockSkew, + NameClaimType = config.NameClaimType, + RoleClaimType = config.RoleClaimType }; options.Events = new JwtBearerEvents { OnTokenValidated = context => { - var nameClaim = context.Principal?.FindFirst(OidcConfigConstants.NameClaimType); - var emailClaim = context.Principal?.FindFirst(OidcConfigConstants.EmailClaimType); + var nameClaim = context.Principal?.FindFirst(config.NameClaimType); + var emailClaim = context.Principal?.FindFirst(config.EmailClaimType); if (nameClaim == null && emailClaim != null) { var identity = context.Principal?.Identity as ClaimsIdentity; - identity?.AddClaim(new Claim(OidcConfigConstants.NameClaimType, emailClaim.Value)); + identity?.AddClaim(new Claim(config.NameClaimType, emailClaim.Value)); } return Task.CompletedTask; } }; - - var httpClientHandler = new HttpClientHandler(); - - if (config.InsecureDevMode) + + var httpClientHandler = new HttpClientHandler { - httpClientHandler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true; - } - + ServerCertificateCustomValidationCallback = (_, _, _, _) => true + }; + var discoveryUrl = $"{config.Issuer}.well-known/openid-configuration"; options.ConfigurationManager = new ConfigurationManager( @@ -224,21 +229,16 @@ static void ValidateConfiguration(AppConfig config) static Task GetUserEmail(HttpContext context, AppConfig config) { - if (config.InsecureDevMode) - { - return Task.FromResult(config.InsecureDevEmail!); - } - if (context.User.Identity?.IsAuthenticated == true) { - var emailClaim = context.User.FindFirst(OidcConfigConstants.EmailClaimType); + var emailClaim = context.User.FindFirst(config.EmailClaimType); if (emailClaim != null) { return Task.FromResult(emailClaim.Value); } } - if (context.Request.Headers.TryGetValue(OidcConfigConstants.AuthRequestEmailHeader, out var emailHeader)) + if (context.Request.Headers.TryGetValue(config.AuthRequestEmailHeader, out var emailHeader)) { return Task.FromResult(emailHeader.ToString()); } @@ -271,23 +271,3 @@ record UserIdentityResponse( string? AuthenticationType, List Claims ); - -static class OidcConfigConstants -{ - // Default environment variable values - public const string DefaultDbHost = "postgres"; - public const string DefaultDbPort = "5440"; - public const string DefaultDbName = "dexdemo"; - public const string DefaultDbUser = "dexdemo"; - public const string DefaultDbPassword = "dexdemo"; - public const string DefaultDexIssuer = "https://dex.127.0.0.1.sslip.io/"; - - // JWT settings - public const string NameClaimType = "name"; - public const string RoleClaimType = "role"; - public const string EmailClaimType = "email"; - public const string AuthRequestEmailHeader = "X-Auth-Request-Email"; - - // Time settings - public static readonly TimeSpan ClockSkew = TimeSpan.FromMinutes(5); -} diff --git a/backend-dotnet/README.md b/backend-dotnet/README.md index a3bb0eb..8e480be 100644 --- a/backend-dotnet/README.md +++ b/backend-dotnet/README.md @@ -13,9 +13,9 @@ dotnet run ```bash docker build -t dex-demo-backend-dotnet:latest . docker run -p 8000:8000 \ - -e DB_HOST=postgres \ - -e DB_PORT=5440 \ - -e DEX_ISSUER=https://dex.127.0.0.1.sslip.io/ \ + -e AppConfig__DbHost=postgres \ + -e AppConfig__DbPort=5440 \ + -e AppConfig__Issuer=https://dex.127.0.0.1.sslip.io/ \ dex-demo-backend-dotnet:latest ``` @@ -34,8 +34,20 @@ docker run -p 8000:8000 \ ## Переменные окружения -Все переменные идентичны Python версии: -- `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` -- `DEX_ISSUER` -- `INSECURE_DEV_MODE`, `INSECURE_DEV_EMAIL` +### База данных +- `AppConfig__DbHost` - хост PostgreSQL (по умолчанию: postgres) +- `AppConfig__DbPort` - порт PostgreSQL (по умолчанию: 5440) +- `AppConfig__DbName` - имя базы данных (по умолчанию: dexdemo) +- `AppConfig__DbUser` - пользователь базы данных (по умолчанию: dexdemo) +- `AppConfig__DbPassword` - пароль базы данных (по умолчанию: dexdemo) + +### Аутентификация +- `AppConfig__Issuer` - URL Dex сервера (по умолчанию: https://dex.127.0.0.1.sslip.io/) + +### Режим разработки +- `AppConfig__InsecureDevMode` - включить небезопасный режим разработки (true/false) +- `AppConfig__InsecureDevEmail` - email для тестирования в режиме разработки + +### CORS +- `AppConfig__AllowedOrigins` - разрешенные origins для CORS (JSON массив)