using DigitalData.Core.API; using DigitalData.Core.Application; using EnvelopeGenerator.Infrastructure; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Localization; using Microsoft.EntityFrameworkCore; using System.Globalization; using Scalar.AspNetCore; using Microsoft.OpenApi.Models; using DigitalData.UserManager.DependencyInjection; using EnvelopeGenerator.Application; using DigitalData.Auth.Client; using DigitalData.Core.Abstractions; using EnvelopeGenerator.GeneratorAPI.Models; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using DigitalData.Core.Abstractions.Security.Extensions; using EnvelopeGenerator.GeneratorAPI.Middleware; using NLog.Web; using NLog; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("Logging initialized!"); try { var builder = WebApplication.CreateBuilder(args); builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); if (!builder.Environment.IsDevelopment()) { builder.Logging.ClearProviders(); builder.Host.UseNLog(); } var config = builder.Configuration; var deferredProvider = new DeferredServiceProvider(); builder.Services.AddControllers(); // CORS Policy var allowedOrigins = config.GetSection("AllowedOrigins").Get() ?? throw new InvalidOperationException("AllowedOrigins section is missing in the configuration."); builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOriginsPolicy", builder => { builder.WithOrigins(allowedOrigins) .SetIsOriginAllowedToAllowWildcardSubdomains() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); }); // Swagger builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "signFLOW Absender-API", Description = "Eine API zur Verwaltung der Erstellung, des Versands und der Nachverfolgung von Umschlägen in der signFLOW-Anwendung.", Contact = new OpenApiContact { Name = "Digital Data GmbH", Url = new Uri("https://digitaldata.works/digitale-signatur#kontakt"), Email = "info-flow@digitaldata.works" }, }); options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Name = "Authorization", Type = SecuritySchemeType.Http, Scheme = "bearer", BearerFormat = "JWT", In = ParameterLocation.Header, Description = "JWT-Autorisierungs-Header unter Verwendung des Bearer-Schemas.", }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, Array.Empty() } }); var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml"); foreach (var xmlFile in xmlFiles) { options.IncludeXmlComments(xmlFile); } }); builder.Services.AddOpenApi(); //AddEF Core dbcontext var useDbMigration = Environment.GetEnvironmentVariable("MIGRATION_TEST_MODE") == true.ToString() || config.GetValue("UseDbMigration"); var cnnStrName = useDbMigration ? "DbMigrationTest" : "Default"; var connStr = config.GetConnectionString(cnnStrName) ?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration."); builder.Services.Configure(cs => cs.Value = connStr); builder.Services.AddDbContext(options => options.UseSqlServer(connStr)); builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams")); var authTokenKeys = config.GetOrDefault(); builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(opt => { opt.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) => { var clientParams = deferredProvider.GetOptions(); var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience); return [publicKey.SecurityKey]; }, ValidateIssuer = true, ValidIssuer = authTokenKeys.Issuer, ValidateAudience = true, ValidAudience = authTokenKeys.Audience, }; opt.Events = new JwtBearerEvents { OnMessageReceived = context => { // if there is no token read related cookie or query string if (context.Token is null) // if there is no token { if (context.Request.Cookies.TryGetValue(authTokenKeys.Cookie, out var cookieToken) && cookieToken is not null) context.Token = cookieToken; else if (context.Request.Query.TryGetValue(authTokenKeys.QueryString, out var queryStrToken)) context.Token = queryStrToken; } return Task.CompletedTask; } }; }); // Authentication builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.Cookie.HttpOnly = true; // Makes the cookie inaccessible to client-side scripts for security options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; // Ensures cookies are sent over HTTPS only options.Cookie.SameSite = SameSiteMode.Strict; // Protects against CSRF attacks by restricting how cookies are sent with requests from external sites options.LoginPath = "/api/auth/login"; options.LogoutPath = "/api/auth/logout"; options.SlidingExpiration = true; }); // User manager #pragma warning disable CS0618 // Type or member is obsolete builder.Services.AddUserManager(); #pragma warning restore CS0618 // Type or member is obsolete // LDAP builder.ConfigureBySection(); builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions")); // Localizer builder.Services.AddCookieBasedLocalizer(); // Envelope generator serives #pragma warning disable CS0618 // Type or member is obsolete builder.Services .AddEnvelopeGeneratorInfrastructureServices(sqlExecutorConfigureOptions: executor => executor.ConnectionString = connStr) .AddEnvelopeGeneratorServices(config); #pragma warning restore CS0618 // Type or member is obsolete var app = builder.Build(); deferredProvider.Factory = () => app.Services; app.UseMiddleware(); app.MapOpenApi(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment() || (app.IsDevOrDiP() && config.GetValue("UseSwagger"))) { app.UseSwagger(); app.UseSwaggerUI(); app.MapScalarApiReference(); } // Set CORS policy app.UseCors("AllowSpecificOriginsPolicy"); // Localizer string[] supportedCultureNames = ["de-DE", "en-US"]; IList list = [.. supportedCultureNames.Select(cn => new CultureInfo(cn))]; var cultureInfo = list.FirstOrDefault() ?? throw new InvalidOperationException("There is no supported culture."); var requestLocalizationOptions = new RequestLocalizationOptions { SupportedCultures = list, SupportedUICultures = list }; requestLocalizationOptions.RequestCultureProviders.Add(new QueryStringRequestCultureProvider()); app.UseRequestLocalization(requestLocalizationOptions); app.UseHttpsRedirection(); app.UseDefaultFiles(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); } catch (Exception ex) { logger.Error(ex, "Stopped program because of exception"); throw; }