Compare commits

..

11 Commits

Author SHA1 Message Date
khwezi f5a69de0a0 Merge pull request 'Refactored CheckSameSite' (#127) from dataprotection into master
Reviewed-on: #127
2026-06-14 22:52:05 +02:00
Khwezi Mngoma 40a5f94941 Refactored CheckSameSite
continuous-integration/drone/pr Build is passing
2026-06-14 22:50:31 +02:00
khwezi fc4db32f20 Merge pull request 'dataprotection' (#126) from dataprotection into master
Reviewed-on: #126
2026-06-14 13:12:20 +02:00
Khwezi Mngoma 9cb4b8264d Refactored Api registration methods
continuous-integration/drone/pr Build is passing
2026-06-14 13:11:40 +02:00
Khwezi Mngoma ddd823afab Configured AddLiteCharmsWebSecurity() to setup ConfigureCookieOidcSameSiteSupport() 2026-06-14 13:09:57 +02:00
khwezi a9aa0a675a Merge pull request 'Added cookie policies on AddLiteCharmsWebSecurity' (#125) from dataprotection into master
Reviewed-on: #125
2026-06-14 12:51:25 +02:00
Khwezi Mngoma 6418d27f5a Added cookie policies on AddLiteCharmsWebSecurity
continuous-integration/drone/pr Build is passing
2026-06-14 12:50:13 +02:00
khwezi a763e5e40e Merge pull request 'Added certificate protected data protection keys' (#124) from dataprotection into master
Reviewed-on: #124
2026-06-14 11:27:32 +02:00
Khwezi Mngoma 9b15e296df Added certificate protected data protection keys
continuous-integration/drone/pr Build is passing
2026-06-14 11:26:20 +02:00
khwezi 1ef5e52ed9 Merge pull request 'Refactored AddDataProtectionDatabase' (#123) from dataprotection into master
Reviewed-on: #123
2026-06-14 10:12:08 +02:00
Khwezi Mngoma f4a615277f Refactored AddDataProtectionDatabase
continuous-integration/drone/pr Build is passing
2026-06-14 10:11:25 +02:00
3 changed files with 51 additions and 22 deletions
+48 -12
View File
@@ -3,8 +3,6 @@ using LiteCharms.Features.Api;
using LiteCharms.Features.Api.Configuration; using LiteCharms.Features.Api.Configuration;
using LiteCharms.Features.Api.Sdk; using LiteCharms.Features.Api.Sdk;
using LiteCharms.Features.Postgres; using LiteCharms.Features.Postgres;
using Microsoft.AspNetCore.Hosting;
using System.Runtime.InteropServices;
namespace LiteCharms.Features.Extensions; namespace LiteCharms.Features.Extensions;
@@ -56,9 +54,14 @@ public static class Api
public static IServiceCollection AddLiteCharmsWebSecurity(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddLiteCharmsWebSecurity(this IServiceCollection services, IConfiguration configuration)
{ {
var certificate = X509CertificateLoader.LoadPkcs12(Convert.FromBase64String(configuration["DataProtection:Certificate"]!), configuration["DataProtection:Password"]);
services.AddDataProtection().PersistKeysToDbContext<DataProtectionDbContext>() services.AddDataProtection().PersistKeysToDbContext<DataProtectionDbContext>()
.ProtectKeysWithCertificate(certificate)
.SetApplicationName("LiteCharmsApp"); .SetApplicationName("LiteCharmsApp");
services.ConfigureCookieOidcSameSiteSupport();
var configSection = configuration.GetSection(nameof(LiteCharmsSettings)); var configSection = configuration.GetSection(nameof(LiteCharmsSettings));
var authOptions = new LiteCharmsSettings(); var authOptions = new LiteCharmsSettings();
@@ -71,39 +74,48 @@ public static class Api
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}) })
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.Name = "LiteCharmsApp.Session";
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{ {
options.Authority = authOptions.Authority; options.Authority = authOptions.Authority;
options.ClientId = authOptions.ClientId; options.ClientId = authOptions.ClientId;
options.ClientSecret = authOptions.ClientSecret; options.ClientSecret = authOptions.ClientSecret;
options.ResponseType = "code"; options.ResponseType = "code";
options.SaveTokens = true; options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = 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.ForwardSignOut = CookieAuthenticationDefaults.AuthenticationScheme;
options.Scope.Clear(); options.Scope.Clear();
options.Scope.Add("openid"); options.Scope.Add("openid");
options.Scope.Add("profile"); options.Scope.Add("profile");
options.Scope.Add("email"); options.Scope.Add("email");
options.Events = new OpenIdConnectEvents options.Events = new OpenIdConnectEvents
{ {
OnRedirectToIdentityProviderForSignOut = context => OnRedirectToIdentityProviderForSignOut = context =>
{ {
var idToken = context.ProtocolMessage.IdTokenHint; var idToken = context.ProtocolMessage.IdTokenHint;
if (string.IsNullOrEmpty(idToken)) if (string.IsNullOrEmpty(idToken))
{ {
var tokens = context.Properties.GetTokens(); var tokens = context.Properties.GetTokens();
var idTokenItem = tokens.FirstOrDefault(t => string.Equals(t.Name, "id_token", StringComparison.Ordinal)); var idTokenItem = tokens.FirstOrDefault(t => string.Equals(t.Name, "id_token", StringComparison.Ordinal));
if (idTokenItem != null) context.ProtocolMessage.IdTokenHint = idTokenItem.Value; if (idTokenItem != null) context.ProtocolMessage.IdTokenHint = idTokenItem.Value;
} }
return Task.CompletedTask; return Task.CompletedTask;
}, },
}; };
@@ -114,6 +126,30 @@ public static class Api
return services; return services;
} }
private static void ConfigureCookieOidcSameSiteSupport(this IServiceCollection services) =>
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
private static void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
if (options.SameSite == SameSiteMode.None)
{
bool isSecure = httpContext.Request.IsHttps;
if (!isSecure && httpContext.Request.Headers.TryGetValue("X-Forwarded-Proto", out var proto))
isSecure = string.Equals(proto, "https", StringComparison.OrdinalIgnoreCase);
if (!isSecure && httpContext.Request.Headers.TryGetValue("Forwarded", out var forwarded))
isSecure = forwarded.ToString().Contains("proto=https", StringComparison.OrdinalIgnoreCase);
if (!isSecure) options.SameSite = SameSiteMode.Unspecified;
}
}
public static IServiceCollection AddLiteCharmsApiSecurity(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddLiteCharmsApiSecurity(this IServiceCollection services, IConfiguration configuration)
{ {
var configSection = configuration.GetSection(nameof(LiteCharmsSettings)); var configSection = configuration.GetSection(nameof(LiteCharmsSettings));
+2 -10
View File
@@ -9,18 +9,10 @@ public static class Postgres
public static IServiceCollection AddDataProtectionDatabase(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddDataProtectionDatabase(this IServiceCollection services, IConfiguration configuration)
{ {
var connectionString = configuration.GetConnectionString(DataProtectionDbConfigName); var connectionString = configuration.GetConnectionString(DataProtectionDbConfigName);
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
dataSourceBuilder.ConfigureTypeLoading(options => { options.EnableTypeLoading(false); });
var dataSource = dataSourceBuilder.Build();
services.AddSingleton(dataSource);
services.AddPooledDbContextFactory<DataProtectionDbContext>(options => services.AddPooledDbContextFactory<DataProtectionDbContext>(options =>
options.UseNpgsql(dataSource)); options.UseNpgsql(connectionString));
return services; return services;
} }
@@ -197,6 +197,7 @@
<!-- Shared Usings --> <!-- Shared Usings -->
<ItemGroup> <ItemGroup>
<Using Include="Microsoft.AspNetCore.DataProtection" /> <Using Include="Microsoft.AspNetCore.DataProtection" />
<Using Include="System.Security.Cryptography.X509Certificates" />
<Using Include="Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage" /> <Using Include="Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage" />
<Using Include="System.Text.Json.Serialization" /> <Using Include="System.Text.Json.Serialization" />
<Using Include="System.Reflection" /> <Using Include="System.Reflection" />