Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/CampusPulse.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using CampusPulse.Api.Push;
using CampusPulse.Api.Users;
using CampusPulse.Infrastructure;
using Microsoft.AspNetCore.DataProtection;

var builder = WebApplication.CreateBuilder(args);

Expand Down Expand Up @@ -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);

Expand Down
27 changes: 27 additions & 0 deletions tests/CampusPulse.Api.Tests/StartupTests.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<Program>
{
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);
}
}

/// <summary>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.</summary>
[Fact]
public async Task Data_Protection_is_ephemeral_by_design()
{
using var factory = new SignInFactory();
await factory.CreateClient().GetAsync("/health");

Assert.IsType<EphemeralDataProtectionProvider>(factory.Services.GetRequiredService<IDataProtectionProvider>());
}
}
Loading