using Microsoft.EntityFrameworkCore; using DigitalData.UserManager.Infrastructure.Repositories; using DigitalData.UserManager.Application; using DigitalData.Core.Application; using Microsoft.AspNetCore.Authentication.Cookies; using NLog.Web; using NLog; using DigitalData.Core.API; using DigitalData.UserManager.API.Controllers; using DigitalData.UserManager.Application.Services; using Microsoft.Data.SqlClient; using Newtonsoft.Json; using Microsoft.IdentityModel.Tokens; using DigitalData.UserManager.Application.DTOs.User; using DigitalData.UserManager.API.Models; using DigitalData.Auth.Client; using DigitalData.UserManager.API; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Options; using DigitalData.Core.Abstractions.Security.Extensions; using Microsoft.OpenApi.Models; using DigitalData.UserManager.DependencyInjection; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Debug("init main"); try { var builder = WebApplication.CreateBuilder(); var config = builder.Configuration; builder.Services.AddEncryptor(builder.Configuration.GetSection("EncryptionParameters")); builder.Logging.ClearProviders(); builder.Host.UseNLog(); builder.Services.AddControllers(opt => { opt.Conventions.Add(new RemoveIfControllerConvention() .AndIf(c => c.ControllerName == nameof(EncryptionController).Replace("Controller", "")) .AndIf(c => !config.GetValue("UseEncryptor"))); }).AddNewtonsoftJson(options => { options.SerializerSettings.DateTimeZoneHandling = config.GetValue("DateTimeZoneHandling"); }); if (builder.Configuration.GetValue("UseSwagger")) { builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); } builder.Services.AddControllers(opt => { opt.Conventions.Add(new RemoveIfControllerConvention() .AndIf(c => c.ControllerName == nameof(EncryptionController).Replace("Controller", "")) .AndIf(c => !config.GetValue("UseEncryptor"))); }); 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"; }); // Once the app is built, the password will be decrypted with Encryptor. lazy loading also acts as a call back method. Lazy? cnn_str = null; builder.Services.AddUserManager(options => options.UseSqlServer(cnn_str!.Value).EnableSensitiveDataLogging()); var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get() ?? throw new InvalidOperationException("In appsettings there is no allowed origin."); builder.Services.AddCors(options => { options.AddPolicy(name: "DefaultCorsPolicy", builder => { builder.WithOrigins(allowedOrigins) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); }); builder.ConfigureBySection(); builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions")); builder.Services.AddJWTService(user => new SecurityTokenDescriptor() { Claims = user.ToClaimList().ToDictionary(claim => claim.Type, claim => claim.Value as object) }); var lazyProvider = new LazyServiceProvider(); builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams")); var authTokenKeys = config.GetSection(nameof(AuthTokenKeys)).Get() ?? new(); 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 = lazyProvider.GetRequiredService>()?.Value; var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience); return new List() { 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; } }; }); builder.Services.AddSwaggerGen(setupAct => { setupAct.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.Http, Scheme = "Bearer" }); setupAct.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, Array.Empty() } }); }); builder.Services.AddCookieBasedLocalizer(); var app = builder.Build(); lazyProvider.Factory = () => app.Services; cnn_str = new(() => { var encryptor = app.Services.GetRequiredService(); var eCnnStr = config.GetConnectionString("UM_DEF") ?? throw new InvalidOperationException("Connection string 'DD_ECM_Connection' is missing from the configuration."); SqlConnectionStringBuilder cnnStrBuilder = new(eCnnStr); cnnStrBuilder.UserID = encryptor.Decrypt(cnnStrBuilder.UserID); cnnStrBuilder.Password = encryptor.Decrypt(cnnStrBuilder.Password); var dCnnStr = cnnStrBuilder.ConnectionString; return dCnnStr; }); app.UseCors("DefaultCorsPolicy"); if (builder.Configuration.GetValue("UseSwagger")) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseCookieBasedLocalizer("de-DE", "en-US"); app.UseDefaultFiles(); app.UseStaticFiles(); app.UseRouting(); app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapDefaultControllerRoute(); app.Run(); } catch (Exception exception) { logger.Error(exception, "Stopped program because of exception"); throw; }