From a274ef448af8b73c660e0a34f75cb9e2dab43336 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Sun, 12 Jul 2026 02:04:03 +0900 Subject: [PATCH] fix: Auth cookies invalidated on every deployment (Data Protection discriminator) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User reported: "배포가 되면 인증이 풀린다" (auth resets after every deployment). Root cause: Program.cs had no explicit Data Protection configuration. Without SetApplicationName, ASP.NET Core derives the key-ring discriminator from the app's physical content root path. Every deployment lands in a brand-new directory (~/deployments/quantengine_{tag}_{hash}/), so the discriminator changed on every single release. The cookie authentication ticket is encrypted/signed via this key ring, so once the discriminator changed, every previously-issued auth cookie became undecryptable -- forcing all logged-in users to authenticate again after each deploy, even well inside their 12-hour ExpireTimeSpan. Fix: explicit .SetApplicationName("QuantEngine") pins a stable discriminator across deployments, and .PersistKeysToFileSystem points at %LOCALAPPDATA%/quantengine-keys (Linux: ~/.local/share/quantengine-keys via User=kjh2064 in the systemd unit) -- a location outside the versioned deployment directories, so the actual key material also survives every redeploy and service restart instead of only the discriminator being stable. --- src/dotnet/QuantEngine.Web/Program.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/dotnet/QuantEngine.Web/Program.cs b/src/dotnet/QuantEngine.Web/Program.cs index 29c94d40..b4c61b8e 100644 --- a/src/dotnet/QuantEngine.Web/Program.cs +++ b/src/dotnet/QuantEngine.Web/Program.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.DataProtection; using QuantEngine.Infrastructure.Data; using QuantEngine.Infrastructure.Repositories; using QuantEngine.Infrastructure.Services; @@ -22,6 +23,22 @@ try var builder = WebApplication.CreateBuilder(args); builder.Host.UseSerilog(); + // Data Protection: without this, ASP.NET Core derives its key-ring + // discriminator from the app's physical content root path. Every + // deployment lands in a brand-new directory + // (~/deployments/quantengine_{tag}_{hash}/), so the discriminator + // changed on every single deploy and every previously-issued auth + // cookie became undecryptable -- forcing all users to log in again + // after each release. SetApplicationName pins a stable discriminator; + // PersistKeysToFileSystem points at a location outside the versioned + // deployment folders so the actual key material also survives restarts. + var dataProtectionKeysPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "quantengine-keys"); + builder.Services.AddDataProtection() + .SetApplicationName("QuantEngine") + .PersistKeysToFileSystem(new DirectoryInfo(dataProtectionKeysPath)); + // Authentication & Authorization builder.Services.AddAuthentication(opts => {