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? InsecureDevEmail { get; set; }
|
||||||
public string[] AllowedOrigins { get; set; } = ["http://localhost:3000", "https://localhost:3000"];
|
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 =>
|
public string ConnectionString =>
|
||||||
$"Host={DbHost};Port={DbPort};Database={DbName};Username={DbUser};Password={DbPassword}";
|
$"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 Dapper;
|
||||||
using DexDemoBackend;
|
using DexDemoBackend;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -8,10 +9,10 @@ using Microsoft.IdentityModel.Protocols;
|
|||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Configure AppConfig using IConfiguration
|
|
||||||
builder.Services.Configure<AppConfig>(builder.Configuration.GetSection("AppConfig"));
|
builder.Services.Configure<AppConfig>(builder.Configuration.GetSection("AppConfig"));
|
||||||
builder.Services.AddSingleton(sp => sp.GetRequiredService<Microsoft.Extensions.Options.IOptions<AppConfig>>().Value);
|
builder.Services.AddSingleton(sp => sp.GetRequiredService<Microsoft.Extensions.Options.IOptions<AppConfig>>().Value);
|
||||||
|
|
||||||
@@ -31,7 +32,6 @@ builder.Services.ConfigureHttpJsonOptions(options =>
|
|||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
|
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
|
||||||
|
|
||||||
// Get current config from DI (supports reload)
|
|
||||||
var currentConfig = app.Services.GetRequiredService<AppConfig>();
|
var currentConfig = app.Services.GetRequiredService<AppConfig>();
|
||||||
|
|
||||||
app.UseCors(policy =>
|
app.UseCors(policy =>
|
||||||
@@ -44,7 +44,6 @@ app.UseCors(policy =>
|
|||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
// Global error handling middleware
|
|
||||||
app.Use(async (context, next) =>
|
app.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -140,6 +139,14 @@ app.Run();
|
|||||||
|
|
||||||
static void ConfigureJwtAuthentication(IServiceCollection services, AppConfig config)
|
static void ConfigureJwtAuthentication(IServiceCollection services, AppConfig config)
|
||||||
{
|
{
|
||||||
|
if (config.InsecureDevMode)
|
||||||
|
{
|
||||||
|
services.AddAuthentication("InsecureDev")
|
||||||
|
.AddScheme<AuthenticationSchemeOptions, InsecureDevAuthenticationHandler>("InsecureDev", _ => { });
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(options =>
|
.AddJwtBearer(options =>
|
||||||
{
|
{
|
||||||
@@ -152,34 +159,32 @@ static void ConfigureJwtAuthentication(IServiceCollection services, AppConfig co
|
|||||||
ValidateAudience = false,
|
ValidateAudience = false,
|
||||||
ValidateLifetime = true,
|
ValidateLifetime = true,
|
||||||
ValidateIssuerSigningKey = true,
|
ValidateIssuerSigningKey = true,
|
||||||
ClockSkew = OidcConfigConstants.ClockSkew,
|
ClockSkew = config.ClockSkew,
|
||||||
NameClaimType = OidcConfigConstants.NameClaimType,
|
NameClaimType = config.NameClaimType,
|
||||||
RoleClaimType = OidcConfigConstants.RoleClaimType
|
RoleClaimType = config.RoleClaimType
|
||||||
};
|
};
|
||||||
|
|
||||||
options.Events = new JwtBearerEvents
|
options.Events = new JwtBearerEvents
|
||||||
{
|
{
|
||||||
OnTokenValidated = context =>
|
OnTokenValidated = context =>
|
||||||
{
|
{
|
||||||
var nameClaim = context.Principal?.FindFirst(OidcConfigConstants.NameClaimType);
|
var nameClaim = context.Principal?.FindFirst(config.NameClaimType);
|
||||||
var emailClaim = context.Principal?.FindFirst(OidcConfigConstants.EmailClaimType);
|
var emailClaim = context.Principal?.FindFirst(config.EmailClaimType);
|
||||||
|
|
||||||
if (nameClaim == null && emailClaim != null)
|
if (nameClaim == null && emailClaim != null)
|
||||||
{
|
{
|
||||||
var identity = context.Principal?.Identity as ClaimsIdentity;
|
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;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var httpClientHandler = new HttpClientHandler();
|
var httpClientHandler = new HttpClientHandler
|
||||||
|
|
||||||
if (config.InsecureDevMode)
|
|
||||||
{
|
{
|
||||||
httpClientHandler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true;
|
ServerCertificateCustomValidationCallback = (_, _, _, _) => true
|
||||||
}
|
};
|
||||||
|
|
||||||
var discoveryUrl = $"{config.Issuer}.well-known/openid-configuration";
|
var discoveryUrl = $"{config.Issuer}.well-known/openid-configuration";
|
||||||
|
|
||||||
@@ -224,21 +229,16 @@ static void ValidateConfiguration(AppConfig config)
|
|||||||
|
|
||||||
static Task<string> GetUserEmail(HttpContext context, AppConfig config)
|
static Task<string> GetUserEmail(HttpContext context, AppConfig config)
|
||||||
{
|
{
|
||||||
if (config.InsecureDevMode)
|
|
||||||
{
|
|
||||||
return Task.FromResult(config.InsecureDevEmail!);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.User.Identity?.IsAuthenticated == true)
|
if (context.User.Identity?.IsAuthenticated == true)
|
||||||
{
|
{
|
||||||
var emailClaim = context.User.FindFirst(OidcConfigConstants.EmailClaimType);
|
var emailClaim = context.User.FindFirst(config.EmailClaimType);
|
||||||
if (emailClaim != null)
|
if (emailClaim != null)
|
||||||
{
|
{
|
||||||
return Task.FromResult(emailClaim.Value);
|
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());
|
return Task.FromResult(emailHeader.ToString());
|
||||||
}
|
}
|
||||||
@@ -271,23 +271,3 @@ record UserIdentityResponse(
|
|||||||
string? AuthenticationType,
|
string? AuthenticationType,
|
||||||
List<ClaimResponse> Claims
|
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
|
```bash
|
||||||
docker build -t dex-demo-backend-dotnet:latest .
|
docker build -t dex-demo-backend-dotnet:latest .
|
||||||
docker run -p 8000:8000 \
|
docker run -p 8000:8000 \
|
||||||
-e DB_HOST=postgres \
|
-e AppConfig__DbHost=postgres \
|
||||||
-e DB_PORT=5440 \
|
-e AppConfig__DbPort=5440 \
|
||||||
-e DEX_ISSUER=https://dex.127.0.0.1.sslip.io/ \
|
-e AppConfig__Issuer=https://dex.127.0.0.1.sslip.io/ \
|
||||||
dex-demo-backend-dotnet:latest
|
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`
|
- `AppConfig__DbHost` - хост PostgreSQL (по умолчанию: postgres)
|
||||||
- `DEX_ISSUER`
|
- `AppConfig__DbPort` - порт PostgreSQL (по умолчанию: 5440)
|
||||||
- `INSECURE_DEV_MODE`, `INSECURE_DEV_EMAIL`
|
- `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