using PostFundManagement.Domain.Api.Configuration; using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Sdk; using PostFundManagement.Domain.Services; using PostFundManagement.Infrastructure.Database; namespace PostFundManagement.Api.Extensions; public static class Security { public static IServiceCollection AddSecurityApiSdk(this IServiceCollection services, IConfiguration configuration) { var configSection = configuration.GetSection(nameof(SecurityClientSettings)); var authOptions = new SecurityClientSettings(); configSection.Bind(authOptions); services.Configure(configSection); if (string.IsNullOrWhiteSpace(authOptions.Authority)) return services; if (!authOptions.Authority.EndsWith("/", StringComparison.Ordinal)) authOptions.Authority += "/"; services.AddRefitClient() .ConfigureHttpClient(config => { config.BaseAddress = new Uri(authOptions.Authority); config.Timeout = TimeSpan.FromSeconds(15); }) .AddStandardResilienceHandler(options => { options.Retry.MaxRetryAttempts = 3; options.Retry.Delay = TimeSpan.FromSeconds(1); options.Retry.BackoffType = Polly.DelayBackoffType.Exponential; }); services.AddScoped(); return services; } public static IServiceCollection AddWebSecurity(this IServiceCollection services, IConfiguration configuration) { services.AddAuthorization(); var certString = configuration["DataProtection:Certificate"] ?? configuration["DataProtection__Certificate"]; var certPassword = configuration["DataProtection:Password"] ?? configuration["DataProtection__Password"]; if (string.IsNullOrEmpty(certString)) throw new InvalidOperationException("Data Protection Certificate configuration is missing."); var certificate = X509CertificateLoader.LoadPkcs12(Convert.FromBase64String(certString), certPassword); services.AddDataProtection().PersistKeysToDbContext() .ProtectKeysWithCertificate(certificate) .SetApplicationName("LiteCharmsApp"); services.Configure(options => options.ApplicationDiscriminator = "LiteCharmsApp"); services.ConfigureCookieOidcSameSiteSupport(); var configSection = configuration.GetSection(nameof(SecuritySettings)); var authOptions = new SecuritySettings(); configSection.Bind(authOptions); services.Configure(configSection); services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.Cookie.SameSite = SameSiteMode.Lax; options.Cookie.Name = "LiteCharmsApp.Session"; }) .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { options.Authority = authOptions.Authority; options.ClientId = authOptions.ClientId; options.ClientSecret = authOptions.ClientSecret; options.ResponseType = "code"; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.CorrelationCookie.SecurePolicy = CookieSecurePolicy.Always; options.CorrelationCookie.SameSite = SameSiteMode.None; options.NonceCookie.SecurePolicy = CookieSecurePolicy.Always; options.NonceCookie.SameSite = SameSiteMode.None; options.ForwardSignOut = CookieAuthenticationDefaults.AuthenticationScheme; options.Scope.Clear(); options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); options.Events = new OpenIdConnectEvents { OnRedirectToIdentityProviderForSignOut = context => { var idToken = context.ProtocolMessage.IdTokenHint; if (string.IsNullOrEmpty(idToken)) { var tokens = context.Properties.GetTokens(); var idTokenItem = tokens.FirstOrDefault(t => string.Equals(t.Name, "id_token", StringComparison.Ordinal)); if (idTokenItem != null) context.ProtocolMessage.IdTokenHint = idTokenItem.Value; } return Task.CompletedTask; }, }; }); services.AddCascadingAuthenticationState(); return services; } }