using System.Globalization; using AspNet.Security.OAuth.Discord; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using SoaFE.Portal.Core; using SoaFE.Portal.Core.Extensions; using SoaFE.Portal.Data; using SoaFE.Portal.Data.Extensions; using SoaFE.Portal.Data.Models; using SoaFE.Portal.Server.Features; using SoaFE.Portal.Server.Features.Auth.Extensions; var builder = WebApplication.CreateBuilder(args); // Configure Blazor builder.Services.AddRazorComponents() .AddInteractiveWebAssemblyComponents() .AddAuthenticationStateSerialization(options => options.SerializeAllClaims = true); builder.Services.AddSoaFEPortalCore(); builder.Services.AddLocalization(); // Configure Authentication builder.Services.AddCascadingAuthenticationState(); builder.Services.AddAuthentication(options => { options.DefaultScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ApplicationScheme; options.DefaultSignOutScheme = IdentityConstants.ApplicationScheme; options.DefaultChallengeScheme = DiscordAuthenticationDefaults.AuthenticationScheme; }) .AddDiscord(options => { options.CallbackPath = "/signin-discord"; options.ClientId = builder.Configuration["OAuth:ClientId"]!; options.ClientSecret = builder.Configuration["OAuth:ClientSecret"]!; options.Scope.Add("identify"); options.ClaimActions.MapCustomJson(Constants.DiscordAvatarClaim, user => { return string.Format( CultureInfo.InvariantCulture, "https://cdn.discordapp.com/avatars/{0}/{1}.{2}", user.GetString("id"), user.GetString("avatar"), user.GetString("avatar")?.StartsWith("a_") ?? false ? "gif" : "png" ); }); }); // Configure Authorization builder.Services.AddAuthorization(); // Configure EF Core builder.Services.AddDbContextFactory(options => { var connectionString = builder.Configuration.GetConnectionString("SoaFEPortalDb"); if (builder.Environment.IsDevelopment()) { // Use detailed errors and sensitive data logging only in development options.EnableDetailedErrors(); options.EnableSensitiveDataLogging(); } if (builder.Environment.IsProduction()) { options.EnableThreadSafetyChecks(false); } options.UseNpgsql(connectionString, npgsqlOptions => { npgsqlOptions.EnableRetryOnFailure( maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(10), errorCodesToAdd: null); npgsqlOptions.MigrationsAssembly("SoaFE.Portal.Data"); npgsqlOptions.MigrationsHistoryTable("_migrations_history"); npgsqlOptions.MapSoaFEPortalEnums(); }); }); if (builder.Environment.IsDevelopment()) { builder.Services.AddDatabaseDeveloperPageExceptionFilter(); } // Configure Identity builder.Services.AddIdentity() .AddEntityFrameworkStores() .AddUserManager>() .AddSignInManager>(); // Configure Features var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Error", createScopeForErrors: true); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true); app.UseHttpsRedirection(); app.UseAntiforgery(); app.MapStaticAssets(); app.UseAuthentication(); app.UseAuthorization(); app.MapAuth(); app.MapRazorComponents() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(SoaFE.Portal.Client._Imports).Assembly); app.Run();