Enhance AppConfig with JWT settings and update Program.cs for improved authentication handling. Modify README.md to reflect new environment variable structure for configuration.
This commit is contained in:
@@ -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}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
36
backend-dotnet/InsecureDevAuthenticationHandler.cs
Normal file
36
backend-dotnet/InsecureDevAuthenticationHandler.cs
Normal file
@@ -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<AuthenticationSchemeOptions>
|
||||
{
|
||||
private readonly AppConfig _config;
|
||||
|
||||
public InsecureDevAuthenticationHandler(
|
||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
AppConfig config) : base(options, logger, encoder)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> 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));
|
||||
}
|
||||
}
|
||||
@@ -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<AppConfig>(builder.Configuration.GetSection("AppConfig"));
|
||||
builder.Services.AddSingleton(sp => sp.GetRequiredService<Microsoft.Extensions.Options.IOptions<AppConfig>>().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<AppConfig>();
|
||||
|
||||
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<AuthenticationSchemeOptions, InsecureDevAuthenticationHandler>("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<OpenIdConnectConfiguration>(
|
||||
@@ -224,21 +229,16 @@ static void ValidateConfiguration(AppConfig config)
|
||||
|
||||
static Task<string> 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<ClaimResponse> 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);
|
||||
}
|
||||
|
||||
@@ -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 массив)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user