using System.Text.Json.Serialization; using Azure.Identity; using LCU.API.Internal.Document.Configurations; using LCU.API.Internal.Document.Models; using LCU.API.Internal.Document.Services; using LCU.API.Internal.DocumentLib.Configurations; using LCU.API.Internal.DocumentLib.Containers; using LCU.API.Internal.DocumentLib.DataAccess; using LCU.API.Internal.DocumentLib.Repositories; using LCU.Library.Internal.Logging; using LCU.Library.Internal.Logging.Middleware; using LCU.Library.Internal.Logging.Models; using Microsoft.OpenApi.Models; using LCU.Library.Internal.ApiSecurity; using Serilog; using LCU.API.Internal.DocumentLib.Utilities; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Web; using MongoDB.Bson.Serialization; using LCU.Library.Internal.Configuration; using Microsoft.FeatureManagement; var MyAllowSpecificOrigins = "_myAllowSpecificOrigins"; var builder = BuildBuilder(args); BuildServicesAndConfigurations(builder); BuildAppAndRun(builder); WebApplicationBuilder BuildBuilder(string[] args) { var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddJsonFile("/app/config/mapped-appsettings/primary.json", true, true); builder.Configuration.AddJsonFile("/vault/secrets/documentapi_configuration.json", true, true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-azure-client-id.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-azure-tenant-id.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-client-id.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-client-secret.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-tenant-id.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-swagger-oauth-scope.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-seq-api-key.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-blob-storage-connection-string.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-mongo-database-connection-string.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-type-uploader-api-key.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-type-retriever-api-key.json", optional: true, reloadOnChange: true); builder.Configuration.AddJsonFile("/vault/secrets/document-api-type-cleanup-api-key.json", optional: true, reloadOnChange: true); if (!bool.Parse(builder.Configuration.GetSection("Primary")["IsAksDeploy"])) { var clientSecretCredentials = new ClientSecretCredential(builder.Configuration["TenantId"], builder.Configuration["ClientId"], builder.Configuration["ClientSecret"]); builder.Configuration.AddAzureKeyVault( new Uri($"https://{builder.Configuration["AzureKeyVaultUrl"]}.vault.azure.net/"), clientSecretCredentials); } builder.Host.UseSerilog(); return builder; } void BuildServicesAndConfigurations(WebApplicationBuilder builder) { var services = builder.Services; var configuration = builder.Configuration; services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(configuration.GetSection("AzureAd")); services.AddAuthorization(options => { options.AddPolicy("user_token", policy => { policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); policy.RequireAuthenticatedUser(); }); options.DefaultPolicy = options.GetPolicy("user_token")!; }); services.AddFeatureManagement(); var documentTypeConfiguration = configuration.Bind(services, "Primary"); configuration.Bind(services, "BlobStorage"); configuration.Bind(services, "MongoDatabase"); configuration.Bind(services); var loggerConfiguration = new LoggingConfiguration(documentTypeConfiguration.AppName, documentTypeConfiguration.SeqApiKey, documentTypeConfiguration.SeqUrl); var lcuLogging = new LcuLogging(loggerConfiguration); lcuLogging.UseLcuLogging(); ServiceRegistrations.AddDocumentServices(services); ServiceRegistrations.AddDocumentDataAccesses(services); ServiceRegistrations.AddDocumentRepositories(services); BsonSerializer.RegisterDiscriminatorConvention(typeof(DocumentAudit), new DocumentAuditDiscriminatorConvention()); services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); services.AddEndpointsApiExplorer(); services.AddSwaggerGen(s => { s.SwaggerDoc("v1", new OpenApiInfo { Title = $"{documentTypeConfiguration.AppName}", Version = "v1" }); s.UseOneOfForPolymorphism(); var authority = $"https://login.microsoftonline.com/{configuration.GetSection("AzureAd")["TenantId"]}"; s.AddSecurityDefinition ( nameof(SecuritySchemeType.OpenIdConnect), new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, Flows = new OpenApiOAuthFlows { AuthorizationCode = new OpenApiOAuthFlow { AuthorizationUrl = new Uri($"{authority}/oauth2/v2.0/authorize"), TokenUrl = new Uri($"{authority}/oauth2/v2.0/token") } } //24.10.2022 these SHOULD have enabled OIDC discovery (the discovery info is returned from this URI) but swagger UI is not displaying the auth options //oidc discovery does work, I have set it up with IdentityServer4 in the past //Type = SecuritySchemeType.OpenIdConnect, //OpenIdConnectUrl = new Uri($"https://login.microsoftonline.com/{configuration.GetSection("AzureAd")["TenantId"]}/v2.0/.well-known/openid-configuration") } ); s.AddSecurityRequirement ( new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = nameof(SecuritySchemeType.OpenIdConnect) } }, Array.Empty() } } ); s.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme { In = ParameterLocation.Header, Name = "ApiKey" }); s.AddSecurityDefinition("ApiClient", new OpenApiSecurityScheme { In = ParameterLocation.Header, Name = "Client" }); s.AddSecurityRequirement ( new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "ApiKey" } }, Array.Empty() } } ); s.AddSecurityRequirement ( new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "ApiClient" } }, Array.Empty() } } ); }); services.AddApiKeySecurity(configuration); } void BuildAppAndRun(WebApplicationBuilder builder) { var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseSwagger(s => { s.PreSerializeFilters.Add((doc, request) => { doc.Servers = new List { new OpenApiServer { Url = $"{request.Scheme}://{request.Host.Value}{request.PathBase}" } }; }); }); app.UseSwaggerUI(c => { c.SwaggerEndpoint("v1/swagger.json", "LCU.API.Internal.Document v1"); c.OAuthClientId(app.Configuration.GetSection("AzureAd")["ClientId"]); c.OAuthUsePkce(); c.OAuthScopes(app.Configuration.GetSection("Swagger")["OAuthScope"]); }); app.UseMiddleware(); app.UseSerilogRequestLogging(options => { options.EnrichDiagnosticContext = RequestResponseLoggingMiddleware.RequestDataParser.EnrichFromRequest; }); app.UseRouting(); app.UseCors(MyAllowSpecificOrigins); app.UseHttpsRedirection(); app.UseAuthentication().UseAuthorization(); app.MapControllers(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); app.Run(); } public static class ServiceRegistrations { public static void AddDocumentServices(IServiceCollection services) { services.AddScoped(p => { const string checkDocumentOwner = "GetDocumentCheckOwnership"; return p.GetRequiredService().IsEnabledAsync(checkDocumentOwner).Result ? DocumentService.Ownership.CheckPersonOrOrgNumber.Strategy : DocumentService.Ownership.Unchecked.Strategy; }); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); } public static void AddDocumentDataAccesses(IServiceCollection services) { services.AddScoped(); services.AddScoped(); } public static void AddDocumentRepositories(IServiceCollection services) { services.AddTransient(); services.AddTransient(); services.AddScoped(); services.AddScoped(); services.AddScoped(); } }