public static void ConfigureWebApp(this WebApplicationBuilder builder) { builder.Host.UseMessaging(); builder.Host.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration .ReadFrom.Configuration(hostingContext.Configuration) .Enrich.WithCorrelationId() .Enrich.WithCorrelationIdHeader() .WriteTo.Console( outputTemplate: "[{Level:u3}][{CorrelationId}][{Properties}] {SourceContext} {Message:lj}{NewLine}{Exception}", theme: ConsoleTheme.None)); var services = builder.Services; var configuration = builder.Configuration; var env = builder.Environment; var opts = new SlackRedisOptions { REDIS_URL = Environment.GetEnvironmentVariable("REDIS_URL") }; var options = new ConfigurationOptions { ClientName = opts.GetRedisUsername, Password = opts.GetRedisPassword, EndPoints = { opts.GetRedisServerHostAndPort } }; var conn = ConnectionMultiplexer.Connect(options); services.AddDataProtection() .PersistKeysToStackExchangeRedis(conn) .SetApplicationName( "fplbot"); // set static so cookies are not encrypted differently after a reboot/deploy. https://github.com/dotnet/aspnetcore/issues/2513#issuecomment-354683162 services.AddControllers() .AddJsonOptions(opts => { opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); var successUri = env.IsProduction() ? "https://www.fplbot.app/success" : "https://test.fplbot.app/success"; var errorUri = env.IsProduction() ? "https://www.fplbot.app/error" : "https://test.fplbot.app/error"; services.AddSlackbotDistribution(c => { c.CLIENT_ID = configuration["CLIENT_ID"]; c.CLIENT_SECRET = configuration["CLIENT_SECRET"]; c.SuccessRedirectUri = $"{successUri}?type=slack"; c.OnSuccess = async(teamId, teamName, s) => { var msg = s.GetService <IMessageSession>(); await msg.Publish(new AppInstalled(teamId, teamName)); }; }); services.AddDiscordBotDistribution(c => { c.CLIENT_ID = configuration["DISCORD_CLIENT_ID"]; c.CLIENT_SECRET = configuration["DISCORD_CLIENT_SECRET"]; c.SuccessRedirectUri = $"{successUri}?type=discord"; c.ErrorRedirectUri = errorUri; c.OnSuccess = async(guildId, guildName, s) => { var msg = s.GetService <IMessageSession>(); await msg.Publish(new AppInstalled(guildId, guildName)); }; }); services.Configure <AnalyticsOptions>(configuration); services.AddReducedHttpClientFactoryLogging(); services.AddStackExchangeRedisCache(o => o.ConfigurationOptions = options); services.AddFplBotSlackWebEndpoints(configuration, conn); services.AddFplBotDiscordWebEndpoints(configuration, conn); services.AddVerifiedEntries(configuration); services.AddMediatR(typeof(WebApplicationBuilderExtensions)); // Used in admin pages: services.AddIndexingServices(configuration, conn); services.AddAuthentication(options => { options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(o => { o.Cookie.Name = "fplbot-admin"; o.AccessDeniedPath = "/forbidden"; o.ReturnUrlParameter = "r"; o.ForwardChallenge = SlackAuthenticationDefaults.AuthenticationScheme; }) .AddSlack(c => { c.ClientId = configuration.GetValue <string>("CLIENT_ID"); c.ClientSecret = configuration.GetValue <string>("CLIENT_SECRET"); c.Scope.Add("identity.team"); c.Events.OnRemoteFailure = r => { var errorMsg = r.Request.Query["error"]; r.Response.Redirect($"/error?msg={errorMsg}"); r.HandleResponse(); return(Task.FromResult(0)); }; }) .AddSlackbotEvents(c => { c.SigningSecret = configuration.GetValue <string>("CLIENT_SIGNING_SECRET"); }) .AddDiscordbotEvents(c => { c.PublicKey = configuration.GetValue <string>("DISCORD_PUBLICKEY"); }); services.AddAuthorization(options => { options.AddPolicy("IsAdmin", b => { b.RequireClaim("urn:slack:team_id", "T016B9N3U7P"); b.RequireClaim("urn:slack:user_id", "U016CP6EPR8", "U0172HKTB08", "U016CSWNXAP"); }); }); var mvcBuilder = services .AddRazorPages() .AddRazorPagesOptions(options => { options.Conventions.AuthorizeFolder("/admin", "IsAdmin"); options.Conventions.AllowAnonymousToPage("/*"); }); if (env.IsDevelopment()) { mvcBuilder.AddRazorRuntimeCompilation(); } services.Configure <RouteOptions>(o => { o.LowercaseQueryStrings = true; o.LowercaseUrls = true; }); services.Configure <ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); services.AddCors(options => { options.AddPolicy(CorsOriginValidator.CustomCorsPolicyName, p => p.SetIsOriginAllowed(CorsOriginValidator.ValidateOrigin).AllowAnyHeader().AllowAnyMethod() ); }); services.AddHttpContextAccessor(); }