- Add per-account lockout + IP rate limiter on local sign-in (A07)
- Emit CSP and security headers on every response (A05)
- Run container as non-root `app`, /data 0700 (A05/A02)
- Stop reflecting raw token-endpoint body into redirect URL (A09)
- Handle missing refresh_token in connect callback without a 500
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause of the production "nothing is interactive" bug. The Dockerfile
restored with only the .csproj present (the layer-cache step) and then ran
`dotnet publish --no-restore`. That combination silently omits the Blazor
framework static assets (wwwroot/_framework/blazor.web.js) from the publish
output, so MapStaticAssets 404s the boot script and no interactive circuit
starts on any page — buttons, dropdowns (role changes) all dead.
Letting publish restore against the full project re-materializes the assets.
Reproduced locally and verified the fix. The SDK pin (10.0.203) was a red
herring and is left as-is for reproducibility.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The previous pin (sdk:10.0.204) doesn't exist on MCR — only the installer
SDK uses that patch. MCR publishes band-2 images up to 10.0.203. Band 2
publishes blazor.web.js correctly (verified locally on 10.0.204), so pin
to the newest available 2xx image, 10.0.203.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SDK 10.0.300 published a static-assets endpoints manifest without
blazor.web.js, so MapStaticAssets returned 404 for the Blazor boot script
in production. With no boot script the interactive circuit never starts
and every page renders static — buttons and dropdowns (e.g. user role
changes) do nothing. SDK 10.0.204 is verified to publish blazor.web.js
(physical file + manifest entry) against the 10.0.8 runtime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two deployment-breaking issues caused 404s on protected pages after a
container recreate:
1. DataProtection keys were stored in the container's ephemeral home dir.
Every redeploy regenerated them, invalidating all auth cookies (users
silently logged out) and — worse — making the app-only certs encrypted
under /data/appcerts undecryptable. Persist keys to /data/dpkeys with a
stable application name so they survive recreates.
2. DefaultChallengeScheme was OpenIdConnect, so a logged-out request to any
[Authorize] Blazor page forced an OIDC challenge. When OIDC is
unconfigured/unreachable the challenge throws and the request 404s, with
no path to the login page. Challenge the cookie scheme instead, which
redirects to /account/login (the combined local + Microsoft page). OIDC
is still triggered explicitly from /account/login/entra.
Also harden the container image:
- Pin base images to exact patch (sdk:10.0.300, aspnet:10.0.8). Floating
:10.0 tags drift; a stale/pre-GA SDK base silently drops blazor.web.js
from the publish manifest, 404ing framework assets in production.
- Install curl and switch the compose healthcheck to it (the aspnet image
ships no wget/curl, so the old healthcheck always reported unhealthy).
Probe /account/login (anonymous, 200) since / now 302-redirects.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>