mirror of
https://github.com/claunia/marechai.git
synced 2026-07-08 17:57:08 +00:00
Implement data protection key persistence to ensure token stability across process restarts
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||
using Microsoft.JSInterop;
|
||||
@@ -57,6 +58,24 @@ public sealed class TokenProvider(ProtectedLocalStorage localStorage)
|
||||
// Circuit not ready or JS error. Leave uninitialized so the next call retries.
|
||||
_cachedToken = null;
|
||||
}
|
||||
catch(CryptographicException)
|
||||
{
|
||||
// The data protection key that encrypted the cached token is no longer in the key
|
||||
// ring (e.g. the key directory was wiped or the app's key ring rotated past it).
|
||||
// Treat the stale ciphertext as "no token" and best-effort delete it so the user
|
||||
// simply ends up logged out instead of seeing a server error every render.
|
||||
_cachedToken = null;
|
||||
_initialized = true;
|
||||
|
||||
try
|
||||
{
|
||||
await localStorage.DeleteAsync(StorageKey);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Best-effort cleanup; ignore JS / prerender failures here.
|
||||
}
|
||||
}
|
||||
|
||||
return _cachedToken;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using MudBlazor.Services;
|
||||
@@ -33,6 +34,7 @@ using Marechai.Services;
|
||||
using Marechai.Shared;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -64,6 +66,21 @@ public class Startup(IConfiguration configuration)
|
||||
{
|
||||
services.AddMudServices();
|
||||
|
||||
// Persist the ASP.NET Core data protection keys to a stable on-disk directory so the
|
||||
// tokens we encrypt into ProtectedLocalStorage survive process restarts. Without this
|
||||
// every restart generates a fresh ephemeral key, which makes the next request from a
|
||||
// returning user crash with "The key {guid} was not found in the key ring" when the
|
||||
// auth state provider tries to unprotect the cached JWT. SetApplicationName pins the
|
||||
// ring to this app so multiple Marechai-family apps cannot accidentally share keys.
|
||||
string keyRingPath = Configuration["DataProtection:KeyRingPath"]
|
||||
?? Path.Combine(AppContext.BaseDirectory, "keys");
|
||||
|
||||
Directory.CreateDirectory(keyRingPath);
|
||||
|
||||
services.AddDataProtection()
|
||||
.PersistKeysToFileSystem(new DirectoryInfo(keyRingPath))
|
||||
.SetApplicationName("Marechai.Blazor");
|
||||
|
||||
services.AddMarechaiEmail(Configuration);
|
||||
|
||||
string apiUrl = Configuration.GetSection("ApiClient:Url").Value ?? "http://localhost:5023";
|
||||
|
||||
Reference in New Issue
Block a user