// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Add Singletons services.AddSingleton <IEnvironmentInformationProvider>(new EnvironmentInformationProvider(RuntimeEnvironment)); // Ensure nothing non-secure passes to any controller if (!RuntimeEnvironment.IsDevelopment() && !RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase) && UseHsts()) { services.Configure <MvcOptions>(o => o.Filters.Add(new RequireHttpsAttribute())); // TODO: We need to register codeswifterstarter.com to the list of hsts preloaded domains https://hstspreload.org/ services.AddHsts(o => { o.MaxAge = TimeSpan.FromDays(365); o.Preload = true; o.IncludeSubDomains = true; }); } if (!RuntimeEnvironment.IsDevelopment() && !RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase) && UseSSL()) { services.AddHttpsRedirection(options => { options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect; }); } services.AddHttpContextAccessor(); services.AddScoped <HttpContextAccessor>(); _serverConfiguration = ConfigurationHelper <ServerConfiguration> .GetConfigurationFromJson(RuntimeEnvironment); services.AddSingleton(_serverConfiguration); // Add AutoMapper services.AddSingleton <IMapper>(new Mapper(new MapperConfiguration(cfg => { cfg.AddProfile <AutoMapperProfile>(); }))); // Add framework services. services.AddTransient <INotificationService, NotificationService>(); services.AddTransient <IFileStorageService, FileStorageService>(); services.AddTransient <ICrudWarningService, CrudWarningService>(); services.AddTransient <IDateTime, MachineDateTime>(); // Register AuthenticatedUserService service services.AddScoped <IAuthenticatedUserService, AuthenticatedUserService>(); services.AddScoped <IAuthManagementService, AuthManagementService>(); // Register all query managers services.AddScopedForClassesEndingWith(typeof(AutoMapperProfile).GetTypeInfo().Assembly, "QueryManager"); // Add MediatR services.AddTransient(typeof(IPipelineBehavior <,>), typeof(RequestPreProcessorBehavior <,>)); services.AddTransient(typeof(IPipelineBehavior <,>), typeof(RequestPerformanceBehaviour <,>)); services.AddTransient(typeof(IPipelineBehavior <,>), typeof(RequestValidationBehavior <,>)); services.AddMediatR(typeof(AutoMapperProfile).GetTypeInfo().Assembly); // Add database context services.AddDbContext <CodeSwifterStarterDbContext>(_serverConfiguration.ConnectionStrings .CodeSwifterStarterDatabase); services.AddScoped(service => (ICodeSwifterStarterDbContext)service.GetService(typeof(CodeSwifterStarterDbContext))); // Add seeder services.AddScoped <CodeSwifterStarterDbSeeder>(); // Add support for proper validation return messages services .AddMvc(options => options.Filters.Add(typeof(CustomExceptionFilterAttribute))) .AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore) .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining <AutoMapperProfile>()); services.AddDataProtection(o => { o.ApplicationDiscriminator = "CodeSwifterStarter"; }); services.Configure <BrotliCompressionProviderOptions> (options => options.Level = CompressionLevel.Fastest); services.Configure <GzipCompressionProviderOptions> (options => options.Level = CompressionLevel.Fastest); services.AddResponseCompression(options => { options.EnableForHttps = true; options.Providers.Add <BrotliCompressionProvider>(); options.Providers.Add <GzipCompressionProvider>(); options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml", "image/jpeg", "image/png", "text/html", "application/javascript", "application/json", "text/json", "text/css" }); }); services.AddControllers(); if (!RuntimeEnvironment.IsDevelopment() && !RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { services.Configure <ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.ForwardLimit = 2; }); } services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; }); services .AddMvc(options => { options.Filters.Add(typeof(CustomExceptionFilterAttribute)); }) .AddNewtonsoftJson() .SetCompatibilityVersion(CompatibilityVersion.Version_3_0); // Add authentication services services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => RuntimeEnvironment.IsProduction() || RuntimeEnvironment.IsStaging(); options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.Authority = _serverConfiguration.SecurityProvider.Authority; options.Audience = _serverConfiguration.SecurityProvider.SingleSignOn.Api.Audience; }); var authDomain = _serverConfiguration.SecurityProvider.Authority; services.AddAuthorization(options => { foreach (var securityPolicy in SecurityPoliciesFactory.Policies) { options.AddPolicy(securityPolicy.Name, policy => { foreach (var permission in securityPolicy.Permissions) { policy.Requirements.Add(new PermissionRequirement(permission.Name, authDomain)); } }); } }); // register the scope authorization handler services.AddScoped <IAuthorizationHandler, PermissionHandler>(); // Consider making this publicly available if (RuntimeEnvironment.IsDevelopment() || RuntimeEnvironment.EnvironmentName.Equals("Local", StringComparison.InvariantCultureIgnoreCase)) { services.AddSwaggerDocument(settings => { settings.Title = "CodeSwifterStarter API"; }); } // Customise default API behavour services.Configure <ApiBehaviorOptions>(options => { if (RuntimeEnvironment.IsProduction() || RuntimeEnvironment.IsStaging()) { options.SuppressModelStateInvalidFilter = true; } }); }