diff --git a/src/CampusPulse.Api/Program.cs b/src/CampusPulse.Api/Program.cs index 5203e44..08bef24 100644 --- a/src/CampusPulse.Api/Program.cs +++ b/src/CampusPulse.Api/Program.cs @@ -7,6 +7,7 @@ using CampusPulse.Api.Push; using CampusPulse.Api.Users; using CampusPulse.Infrastructure; +using Microsoft.AspNetCore.DataProtection; var builder = WebApplication.CreateBuilder(args); @@ -46,6 +47,11 @@ // Push notifications through Expo, and the background check of their receipts (see PushSetup). builder.Services.AddCampusPulsePush(); +// AddAuthentication registers Data Protection, which nothing here uses: bearer tokens only, no +// cookies or antiforgery. Ephemeral keys say so, and keep the startup log free of warnings about +// unpersisted keys. A feature that must read protected data after a restart persists keys first. +builder.Services.AddDataProtection().UseEphemeralDataProtectionProvider(); + // Microsoft sign-in (UPT accounts). Unconfigured, it fails closed: see AuthSetup. var auth = builder.Services.AddCampusPulseAuth(builder.Configuration); diff --git a/tests/CampusPulse.Api.Tests/StartupTests.cs b/tests/CampusPulse.Api.Tests/StartupTests.cs index 9419c91..05c0472 100644 --- a/tests/CampusPulse.Api.Tests/StartupTests.cs +++ b/tests/CampusPulse.Api.Tests/StartupTests.cs @@ -1,5 +1,7 @@ +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; namespace CampusPulse.Api.Tests; @@ -27,4 +29,29 @@ public void A_real_start_without_a_connection_string_fails_fast() Assert.Contains("ConnectionStrings__Postgres is not set", exception.ToString()); } + + private sealed class SignInFactory : WebApplicationFactory + { + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.UseEnvironment("Testing"); + builder.UseSetting("ConnectionStrings:Postgres", + "Host=127.0.0.1;Port=1;Database=unused;Username=unused;Password=unused;Timeout=2"); + TestAuth.Configure(builder); + } + } + + /// Data Protection comes with AddAuthentication but protects nothing here (bearer tokens + /// only), so its keys are ephemeral on purpose. Left to its defaults, a service user with no home + /// directory (the Pi's) gets an in-memory key repository and three warnings at every start (#39). + /// The warnings themselves can't be reproduced here: a test process has a home directory, so the + /// defaults pick a file repository instead. This pins the configuration that avoids them. + [Fact] + public async Task Data_Protection_is_ephemeral_by_design() + { + using var factory = new SignInFactory(); + await factory.CreateClient().GetAsync("/health"); + + Assert.IsType(factory.Services.GetRequiredService()); + } }