diff --git a/backend-dotnet/AppConfig.cs b/backend-dotnet/AppConfig.cs index ba530c3..d1da523 100644 --- a/backend-dotnet/AppConfig.cs +++ b/backend-dotnet/AppConfig.cs @@ -2,16 +2,17 @@ namespace DexDemoBackend; public class AppConfig { - public string DbHost { get; init; } = default!; - public string DbPort { get; init; } = default!; - public string DbName { get; init; } = default!; - public string DbUser { get; init; } = default!; - public string DbPassword { get; init; } = default!; - public string Issuer { get; init; } = default!; - public bool InsecureDevMode { get; init; } - public string? InsecureDevEmail { get; init; } - - public string ConnectionString => + public string DbHost { get; set; } = "postgres"; + public string DbPort { get; set; } = "5440"; + public string DbName { get; set; } = "dexdemo"; + public string DbUser { get; set; } = "dexdemo"; + public string DbPassword { get; set; } = "dexdemo"; + public string Issuer { get; set; } = "https://dex.127.0.0.1.sslip.io/"; + public bool InsecureDevMode { get; set; } + public string? InsecureDevEmail { get; set; } + public string[] AllowedOrigins { get; set; } = ["http://localhost:3000", "https://localhost:3000"]; + + public string ConnectionString => $"Host={DbHost};Port={DbPort};Database={DbName};Username={DbUser};Password={DbPassword}"; } diff --git a/backend-dotnet/Program.cs b/backend-dotnet/Program.cs index 2642a66..b7ab35d 100644 --- a/backend-dotnet/Program.cs +++ b/backend-dotnet/Program.cs @@ -3,383 +3,291 @@ using DexDemoBackend; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Tokens; using Npgsql; -using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; var builder = WebApplication.CreateBuilder(args); -// Logging configuration is now handled in appsettings.json -// Only add console logging if not already configured -if (!builder.Logging.Services.Any(s => s.ServiceType == typeof(ILoggerProvider))) -{ - builder.Logging.AddConsole(); -} +// Configure AppConfig using IConfiguration +builder.Services.Configure(builder.Configuration.GetSection("AppConfig")); +builder.Services.AddSingleton(sp => sp.GetRequiredService>().Value); -// Logger will be created after app is built +var config = builder.Configuration.GetSection("AppConfig").Get() ?? new AppConfig(); -// // Force binding to all interfaces in Kubernetes -// Console.WriteLine($"ASPNETCORE_URLS env var: {Environment.GetEnvironmentVariable("ASPNETCORE_URLS")}"); -// Console.WriteLine($"ASPNETCORE_HTTP_PORTS env var: {Environment.GetEnvironmentVariable("ASPNETCORE_HTTP_PORTS")}"); +ValidateConfiguration(config); -// // Clear any existing configuration and force our URL -// builder.WebHost.ConfigureKestrel(options => -// { -// options.ListenAnyIP(8000); -// }); - -// Configuration -var config = new AppConfig -{ - DbHost = Environment.GetEnvironmentVariable("DB_HOST") ?? "postgres", - DbPort = Environment.GetEnvironmentVariable("DB_PORT") ?? "5440", - DbName = Environment.GetEnvironmentVariable("DB_NAME") ?? "dexdemo", - DbUser = Environment.GetEnvironmentVariable("DB_USER") ?? "dexdemo", - DbPassword = Environment.GetEnvironmentVariable("DB_PASSWORD") ?? "dexdemo", - Issuer = Environment.GetEnvironmentVariable("DEX_ISSUER") ?? "https://dex.127.0.0.1.sslip.io/", - InsecureDevMode = Environment.GetEnvironmentVariable("INSECURE_DEV_MODE")?.ToLower() == "true", - InsecureDevEmail = Environment.GetEnvironmentVariable("INSECURE_DEV_EMAIL") -}; - -// Configuration will be logged after logger is created - -builder.Services.AddSingleton(config); - -// Configure JWT Authentication -builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer(options => - { - options.Authority = config.Issuer; - options.RequireHttpsMetadata = false; // For development - options.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuer = true, - ValidIssuer = config.Issuer, - ValidateAudience = false, // Можно включить, если DEX выдает audience - ValidateLifetime = true, - ValidateIssuerSigningKey = true, - ClockSkew = TimeSpan.FromMinutes(5), // Допуск 5 минут на разницу времени - - // Настройка для заполнения User.Identity.Name - NameClaimType = "name", // Используем claim "name" для User.Identity.Name - RoleClaimType = "role" // Используем claim "role" для ролей - }; - - // Add detailed event logging for JWT authentication - options.Events = new JwtBearerEvents - { - OnAuthenticationFailed = context => - { - var jwtLogger = context.HttpContext.RequestServices.GetRequiredService>(); - jwtLogger.LogError(context.Exception, "[JWT Auth Failed] Exception: {Message}", context.Exception?.Message); - jwtLogger.LogDebug("[JWT Auth Failed] StackTrace: {StackTrace}", context.Exception?.StackTrace); - return Task.CompletedTask; - }, - OnTokenValidated = context => - { - var jwtLogger = context.HttpContext.RequestServices.GetRequiredService>(); - - // Логируем все доступные claims для отладки - var claims = string.Join(", ", context.Principal?.Claims?.Select(c => $"{c.Type}={c.Value}") ?? new string[0]); - jwtLogger.LogDebug("[JWT Token Validated] All claims: {Claims}", claims); - - // Проверяем, есть ли name claim, если нет - используем email как fallback - var nameClaim = context.Principal?.FindFirst("name"); - var emailClaim = context.Principal?.FindFirst("email"); - var subClaim = context.Principal?.FindFirst("sub"); - - if (nameClaim == null && emailClaim != null) - { - // Если нет name claim, но есть email - добавляем его как name claim - var identity = context.Principal?.Identity as ClaimsIdentity; - identity?.AddClaim(new Claim("name", emailClaim.Value)); - jwtLogger.LogDebug("[JWT Token Validated] Added email as name claim: {Email}", emailClaim.Value); - } - - jwtLogger.LogInformation("[JWT Token Validated] User.Identity.Name: {UserName}", context.Principal?.Identity?.Name); - jwtLogger.LogInformation("[JWT Token Validated] Email claim: {Email}", emailClaim?.Value ?? "Not found"); - jwtLogger.LogInformation("[JWT Token Validated] Sub claim: {Sub}", subClaim?.Value ?? "Not found"); - - return Task.CompletedTask; - }, - OnMessageReceived = context => - { - var jwtLogger = context.HttpContext.RequestServices.GetRequiredService>(); - jwtLogger.LogDebug("[JWT Message Received] Scheme: {Scheme}, Path: {Path}, Method: {Method}", - context.Scheme.Name, context.Request.Path, context.Request.Method); - - // Log Authorization header (without sensitive token data) - if (context.Request.Headers.TryGetValue("Authorization", out var authHeader)) - { - var authValue = authHeader.ToString(); - if (authValue.StartsWith("Bearer ")) - { - var tokenPreview = authValue.Substring(7, Math.Min(20, authValue.Length - 7)) + "..."; - jwtLogger.LogDebug("[JWT Message Received] Token preview: {TokenPreview}", tokenPreview); - } - } - - return Task.CompletedTask; - }, - OnChallenge = context => - { - var jwtLogger = context.HttpContext.RequestServices.GetRequiredService>(); - jwtLogger.LogWarning("[JWT Challenge] Error: {Error}, ErrorDescription: {ErrorDescription}, ErrorUri: {ErrorUri}", - context.Error, context.ErrorDescription, context.ErrorUri); - return Task.CompletedTask; - } - }; - - // Configure OpenID Connect discovery - var httpClientHandler = new HttpClientHandler(); - - // В режиме разработки отключаем проверку SSL - // В продакшене это должно быть настроено правильно! - if (config.InsecureDevMode) - { - // Logging will be done after logger is created - httpClientHandler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true; - } - - var discoveryUrl = $"{config.Issuer}.well-known/openid-configuration"; - // Logging will be done after logger is created - - options.ConfigurationManager = new ConfigurationManager( - discoveryUrl, - new OpenIdConnectConfigurationRetriever(), - new HttpDocumentRetriever(new HttpClient(httpClientHandler)) - { - RequireHttps = !config.InsecureDevMode - } - ); - }); +ConfigureJwtAuthentication(builder.Services, config); builder.Services.AddAuthorization(); -// Configure JSON serialization to use snake_case builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.SnakeCaseLower; }); -builder.Services.AddCors(options => -{ - options.AddDefaultPolicy(policy => - { - policy.AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader(); - }); -}); - var app = builder.Build(); Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; -// Create logger after app is built -var logger = app.Services.GetRequiredService>(); +// Get current config from DI (supports reload) +var currentConfig = app.Services.GetRequiredService(); -// Log configuration (without sensitive data) -logger.LogInformation("[Config] Application configuration:"); -logger.LogInformation("[Config] DB_HOST: {DbHost}", config.DbHost); -logger.LogInformation("[Config] DB_PORT: {DbPort}", config.DbPort); -logger.LogInformation("[Config] DB_NAME: {DbName}", config.DbName); -logger.LogInformation("[Config] DB_USER: {DbUser}", config.DbUser); -logger.LogInformation("[Config] DB_PASSWORD: [HIDDEN]"); -logger.LogInformation("[Config] DEX_ISSUER: {Issuer}", config.Issuer); -logger.LogInformation("[Config] INSECURE_DEV_MODE: {InsecureDevMode}", config.InsecureDevMode); -logger.LogInformation("[Config] INSECURE_DEV_EMAIL: {InsecureDevEmail}", config.InsecureDevEmail ?? "[NOT SET]"); - -// Log JWT configuration -if (config.InsecureDevMode) +app.UseCors(policy => { - logger.LogWarning("[JWT Config] InsecureDevMode enabled - disabling SSL certificate validation"); -} -logger.LogInformation("[JWT Config] OpenID Connect discovery URL: {DiscoveryUrl}", $"{config.Issuer}.well-known/openid-configuration"); -logger.LogInformation("[JWT Config] RequireHttps: {RequireHttps}", !config.InsecureDevMode); - -app.UseCors(); + policy.WithOrigins(currentConfig.AllowedOrigins) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials(); +}); app.UseAuthentication(); app.UseAuthorization(); -logger.LogInformation("[App] Application middleware configured:"); -logger.LogInformation("[App] CORS: Enabled"); -logger.LogInformation("[App] Authentication: JWT Bearer"); -logger.LogInformation("[App] Authorization: Enabled"); - -// Health check endpoint -app.MapGet("/api/health", () => Results.Ok(new { status = "ok" })); - -// User identity demo endpoint -app.MapGet("/api/user-identity", [Authorize] (HttpContext context, [FromServices] ILogger logger) => +// Global error handling middleware +app.Use(async (context, next) => { - logger.LogInformation("[UserIdentity] User.Identity.Name: {Name}", context.User.Identity?.Name ?? "NULL"); - logger.LogInformation("[UserIdentity] User.Identity.IsAuthenticated: {IsAuthenticated}", context.User.Identity?.IsAuthenticated); - logger.LogInformation("[UserIdentity] User.Identity.AuthenticationType: {AuthType}", context.User.Identity?.AuthenticationType); - - return Results.Ok(new - { - name = context.User.Identity?.Name, - isAuthenticated = context.User.Identity?.IsAuthenticated, - authenticationType = context.User.Identity?.AuthenticationType, - claims = context.User.Claims.Select(c => new { type = c.Type, value = c.Value }).ToList() - }); -}); - -// User info endpoint -app.MapGet("/api/user-info", [Authorize] async (HttpContext context, [FromServices] AppConfig cfg, [FromServices] ILogger userInfoLogger) => -{ - userInfoLogger.LogInformation("[UserInfo] Starting user info request for path: {Path}", context.Request.Path); - userInfoLogger.LogInformation("[UserInfo] User authenticated: {IsAuthenticated}", context.User.Identity?.IsAuthenticated); - try { - var email = await GetUserEmail(context, cfg, userInfoLogger); - userInfoLogger.LogInformation("[UserInfo] Retrieved email: {Email}", email); - + await next(); + } + catch (UnauthorizedAccessException) + { + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + await context.Response.WriteAsJsonAsync(new { error = "Unauthorized" }); + } + catch (Exception) + { + context.Response.StatusCode = StatusCodes.Status500InternalServerError; + await context.Response.WriteAsJsonAsync(new { error = "Internal server error" }); + } +}); + +app.MapGet("/api/health", () => Results.Ok(new HealthResponse("ok"))); + +app.MapGet("/api/user-identity", [Authorize] (HttpContext context) => +{ + return Results.Ok(new UserIdentityResponse( + context.User.Identity?.Name, + context.User.Identity?.IsAuthenticated ?? false, + context.User.Identity?.AuthenticationType, + context.User.Claims.Select(c => new ClaimResponse(c.Type, c.Value)).ToList() + )); +}); + +app.MapGet("/api/user-info", [Authorize] async (HttpContext context, [FromServices] AppConfig cfg) => +{ + try + { + var email = await GetUserEmail(context, cfg); + await using var conn = new NpgsqlConnection(cfg.ConnectionString); await conn.OpenAsync(); - userInfoLogger.LogDebug("[UserInfo] Database connection opened successfully"); - // Get user info - userInfoLogger.LogDebug("[UserInfo] Querying user info for email: {Email}", email); - var userResult = await conn.QuerySingleOrDefaultAsync(@" - SELECT u.email, u.full_name, u.organization_id, o.name as org_name + var userData = await conn.QueryAsync(@" + SELECT + u.email, u.full_name, + o.id as org_id, o.name as org_name, + r.id as role_id, r.name as role_name, r.description as role_description, + l.id as link_id, l.title as link_title, l.url as link_url, l.description as link_description FROM users u LEFT JOIN organizations o ON u.organization_id = o.id - WHERE u.email = @email", + LEFT JOIN user_roles ur ON u.id = ur.user_id + LEFT JOIN roles r ON ur.role_id = r.id + LEFT JOIN role_links rl ON r.id = rl.role_id + LEFT JOIN links l ON rl.link_id = l.id + WHERE u.email = @email + ORDER BY l.id", new { email }); - - if (userResult is null) + + if (!userData.Any()) { - userInfoLogger.LogWarning("[UserInfo] User not found in database for email: {Email}", email); return Results.NotFound(new { detail = "User not found in database" }); } - userInfoLogger.LogInformation("[UserInfo] User found: {Email}, FullName: {FullName}, OrgId: {OrgId}", - userResult.Email, userResult.FullName, userResult.OrganizationId); - - var organization = userResult.OrganizationId.HasValue && userResult.OrgName != null - ? new Organization(userResult.OrganizationId.Value, userResult.OrgName) + var firstRecord = userData.First(); + var organization = firstRecord.OrgId.HasValue && firstRecord.OrgName != null + ? new Organization(firstRecord.OrgId.Value, firstRecord.OrgName) : null; - // Get user roles - userInfoLogger.LogDebug("[UserInfo] Querying user roles for email: {Email}", email); - var roles = (await conn.QueryAsync(@" - SELECT r.id, r.name, r.description - FROM roles r - JOIN user_roles ur ON r.id = ur.role_id - JOIN users u ON ur.user_id = u.id - WHERE u.email = @email", - new { email })).ToList(); + var roles = userData + .Where(x => x.RoleId.HasValue) + .GroupBy(x => x.RoleId) + .Select(g => g.First()) + .Select(x => new Role(x.RoleId!.Value, x.RoleName!, x.RoleDescription)) + .ToList(); - userInfoLogger.LogInformation("[UserInfo] Found {RoleCount} roles for user", roles.Count); + var links = userData + .Where(x => x.LinkId.HasValue) + .GroupBy(x => x.LinkId) + .Select(g => g.First()) + .Select(x => new Link(x.LinkId!.Value, x.LinkTitle!, x.LinkUrl!, x.LinkDescription)) + .ToList(); - // Get available links - userInfoLogger.LogDebug("[UserInfo] Querying available links for email: {Email}", email); - var links = (await conn.QueryAsync(@" - SELECT DISTINCT l.id, l.title, l.url, l.description - FROM links l - JOIN role_links rl ON l.id = rl.link_id - JOIN user_roles ur ON rl.role_id = ur.role_id - JOIN users u ON ur.user_id = u.id - WHERE u.email = @email - ORDER BY l.id", - new { email })).ToList(); - - userInfoLogger.LogInformation("[UserInfo] Found {LinkCount} links for user", links.Count); - userInfoLogger.LogInformation("[UserInfo] Returning user info successfully"); - - return Results.Ok(new UserInfo(userResult.Email, userResult.FullName, organization, roles, links)); + return Results.Ok(new UserInfo(firstRecord.Email, firstRecord.FullName, organization, roles, links)); } - catch (Exception ex) + catch (NpgsqlException ex) when (ex.IsTransient) { - userInfoLogger.LogError(ex, "[UserInfo] Error occurred: {Message}", ex.Message); - throw; + return Results.Json(new { error = "Database temporarily unavailable" }, statusCode: 503); + } + catch (NpgsqlException) + { + return Results.Json(new { error = "Database error" }, statusCode: 500); } }); -logger.LogInformation("[App] Application starting..."); -logger.LogInformation("[App] Available endpoints:"); -logger.LogInformation("[App] GET /api/health - Health check"); -logger.LogInformation("[App] GET /api/user-identity - User identity information (requires authentication)"); -logger.LogInformation("[App] GET /api/user-info - User information (requires authentication)"); - app.Run(); -static Task GetUserEmail(HttpContext context, AppConfig config, ILogger logger) +static void ConfigureJwtAuthentication(IServiceCollection services, AppConfig config) { - logger.LogDebug("[GetUserEmail] Starting authentication check for path: {Path}", context.Request.Path); - logger.LogDebug("[GetUserEmail] Request method: {Method}", context.Request.Method); - logger.LogDebug("[GetUserEmail] User.Identity.IsAuthenticated: {IsAuthenticated}", context.User.Identity?.IsAuthenticated); - logger.LogInformation("[GetUserEmail] User.Identity.Name: {Name}", context.User.Identity?.Name ?? "NULL"); - logger.LogDebug("[GetUserEmail] User.Identity.AuthenticationType: {AuthType}", context.User.Identity?.AuthenticationType); - - // Log all claims - if (context.User.Identity?.IsAuthenticated == true) - { - logger.LogDebug("[GetUserEmail] All user claims:"); - foreach (var claim in context.User.Claims) + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => { - logger.LogDebug("[GetUserEmail] {ClaimType} = {ClaimValue}", claim.Type, claim.Value); - } - } - - // Log all headers - logger.LogDebug("[GetUserEmail] Request headers:"); - foreach (var header in context.Request.Headers) - { - if (header.Key.ToLower().Contains("auth") || header.Key.ToLower().Contains("email")) - { - logger.LogDebug("[GetUserEmail] {HeaderKey} = {HeaderValue}", header.Key, header.Value); - } - } + options.Authority = config.Issuer; + options.RequireHttpsMetadata = false; + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidIssuer = config.Issuer, + ValidateAudience = false, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ClockSkew = OidcConfigConstants.ClockSkew, + NameClaimType = OidcConfigConstants.NameClaimType, + RoleClaimType = OidcConfigConstants.RoleClaimType + }; + + options.Events = new JwtBearerEvents + { + OnTokenValidated = context => + { + var nameClaim = context.Principal?.FindFirst(OidcConfigConstants.NameClaimType); + var emailClaim = context.Principal?.FindFirst(OidcConfigConstants.EmailClaimType); - // Dev mode + if (nameClaim == null && emailClaim != null) + { + var identity = context.Principal?.Identity as ClaimsIdentity; + identity?.AddClaim(new Claim(OidcConfigConstants.NameClaimType, emailClaim.Value)); + } + + return Task.CompletedTask; + } + }; + + var httpClientHandler = new HttpClientHandler(); + + if (config.InsecureDevMode) + { + httpClientHandler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true; + } + + var discoveryUrl = $"{config.Issuer}.well-known/openid-configuration"; + + options.ConfigurationManager = new ConfigurationManager( + discoveryUrl, + new OpenIdConnectConfigurationRetriever(), + new HttpDocumentRetriever(new HttpClient(httpClientHandler)) + { + RequireHttps = !config.InsecureDevMode + } + ); + }); +} + + +static void ValidateConfiguration(AppConfig config) +{ + if (string.IsNullOrWhiteSpace(config.DbHost)) + throw new InvalidOperationException("Configuration parameter 'AppConfig:DbHost' is required"); + + if (string.IsNullOrWhiteSpace(config.DbPort)) + throw new InvalidOperationException("Configuration parameter 'AppConfig:DbPort' is required"); + + if (string.IsNullOrWhiteSpace(config.DbName)) + throw new InvalidOperationException("Configuration parameter 'AppConfig:DbName' is required"); + + if (string.IsNullOrWhiteSpace(config.DbUser)) + throw new InvalidOperationException("Configuration parameter 'AppConfig:DbUser' is required"); + + if (string.IsNullOrWhiteSpace(config.DbPassword)) + throw new InvalidOperationException("Configuration parameter 'AppConfig:DbPassword' is required"); + + if (string.IsNullOrWhiteSpace(config.Issuer)) + throw new InvalidOperationException("Configuration parameter 'AppConfig:Issuer' is required"); + + if (!Uri.TryCreate(config.Issuer, UriKind.Absolute, out _)) + throw new InvalidOperationException("Configuration parameter 'AppConfig:Issuer' must be a valid URL"); + + if (config.InsecureDevMode && string.IsNullOrWhiteSpace(config.InsecureDevEmail)) + throw new InvalidOperationException("Configuration parameter 'AppConfig:InsecureDevEmail' is required when 'AppConfig:InsecureDevMode' is enabled"); +} + +static Task GetUserEmail(HttpContext context, AppConfig config) +{ if (config.InsecureDevMode) { - logger.LogInformation("[GetUserEmail] INSECURE_DEV_MODE: Using email {Email}", config.InsecureDevEmail); return Task.FromResult(config.InsecureDevEmail!); } - // Try JWT token from authenticated user if (context.User.Identity?.IsAuthenticated == true) { - logger.LogDebug("[GetUserEmail] Checking JWT token claims for email"); - var emailClaim = context.User.FindFirst("email"); + var emailClaim = context.User.FindFirst(OidcConfigConstants.EmailClaimType); if (emailClaim != null) { - logger.LogInformation("[GetUserEmail] Found email in JWT claim: {Email}", emailClaim.Value); return Task.FromResult(emailClaim.Value); } - else - { - logger.LogWarning("[GetUserEmail] No email claim found in JWT token"); - } - } - else - { - logger.LogDebug("[GetUserEmail] User is not authenticated via JWT"); } - // Try OAuth2 proxy header - logger.LogDebug("[GetUserEmail] Checking OAuth2 proxy headers"); - if (context.Request.Headers.TryGetValue("X-Auth-Request-Email", out var emailHeader)) + if (context.Request.Headers.TryGetValue(OidcConfigConstants.AuthRequestEmailHeader, out var emailHeader)) { - logger.LogInformation("[GetUserEmail] Found email in OAuth2 proxy header: {Email}", emailHeader.ToString()); return Task.FromResult(emailHeader.ToString()); } - else - { - logger.LogDebug("[GetUserEmail] No X-Auth-Request-Email header found"); - } - logger.LogError("[GetUserEmail] No authentication information found - throwing UnauthorizedAccessException"); throw new UnauthorizedAccessException("No authentication information found"); } -// Internal query result model -record UserQueryResult(string Email, string FullName, int? OrganizationId, string? OrgName); \ No newline at end of file +record UserDataResult( + string Email, + string FullName, + int? OrgId, + string? OrgName, + int? RoleId, + string? RoleName, + string? RoleDescription, + int? LinkId, + string? LinkTitle, + string? LinkUrl, + string? LinkDescription +); + +// API Response Models +record HealthResponse(string Status); + +record ClaimResponse(string Type, string Value); + +record UserIdentityResponse( + string? Name, + bool IsAuthenticated, + 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/appsettings.Development.json b/backend-dotnet/appsettings.Development.json index b097776..3900e2e 100644 --- a/backend-dotnet/appsettings.Development.json +++ b/backend-dotnet/appsettings.Development.json @@ -14,6 +14,17 @@ "Microsoft.AspNetCore": "Information" } }, - "Urls": "http://localhost:8000" + "Urls": "http://localhost:8000", + "AppConfig": { + "DbHost": "localhost", + "DbPort": "5432", + "DbName": "dexdemo", + "DbUser": "dexdemo", + "DbPassword": "dexdemo", + "Issuer": "https://dex.127.0.0.1.sslip.io/", + "InsecureDevMode": true, + "InsecureDevEmail": "test@example.com", + "AllowedOrigins": ["http://localhost:3000", "https://localhost:3000", "http://localhost:5173", "https://localhost:5173"] + } } diff --git a/backend-dotnet/appsettings.Production.json b/backend-dotnet/appsettings.Production.json index e706b49..df7eb3a 100644 --- a/backend-dotnet/appsettings.Production.json +++ b/backend-dotnet/appsettings.Production.json @@ -14,7 +14,17 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "AppConfig": { + "DbHost": "postgres", + "DbPort": "5432", + "DbName": "dexdemo", + "DbUser": "dexdemo", + "DbPassword": "dexdemo", + "Issuer": "https://dex.127.0.0.1.sslip.io/", + "InsecureDevMode": false, + "AllowedOrigins": ["https://yourdomain.com"] + } } diff --git a/backend-dotnet/appsettings.json b/backend-dotnet/appsettings.json index 3285986..a4b9390 100644 --- a/backend-dotnet/appsettings.json +++ b/backend-dotnet/appsettings.json @@ -13,6 +13,16 @@ "Microsoft.Hosting.Lifetime": "Information" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "AppConfig": { + "DbHost": "postgres", + "DbPort": "5440", + "DbName": "dexdemo", + "DbUser": "dexdemo", + "DbPassword": "dexdemo", + "Issuer": "https://dex.127.0.0.1.sslip.io/", + "InsecureDevMode": false, + "AllowedOrigins": ["http://localhost:3000", "https://localhost:3000"] + } }