Commit graph Bifrost/src/Bifrost.Web/Features/Heimdall/HeimdallAuthService.cs
Author SHA1 Message Date
fc8242f655 Suspension survives sign-in; admin-only Suspend/Unsuspend with a receipt (#53) 2026-11-11 08:38:09 +00:00
Heimdall
58fa80bc5b Make the attempt cookie's deletion Secure so a browser honours it
A __Host- cookie is only removed by a Set-Cookie that is itself Secure. The
hardening test now checks Secure on every header, deletion included, and the
completion tests pin that success and refusal both clear the attempt cookie
with Secure, Path=/ and no Domain.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 11:24:02 +00:00
Heimdall
c757acf525 Make every cookie host-only: __Host- names, Secure, Path=/, no Domain
Session, OAuth correlation, antiforgery, voter and Heimdall attempt cookies all take the
__Host- prefix. The prefix forbids a narrower Path, so the voter (was /mill) and attempt
(was /auth/heimdall) cookies move to Path=/.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 18:42:43 +00:00
Meta Vens
b7e3bdf28d Heimdall sign-in: the attempt reader takes no optional nonce; ownership is one ordinal comparison
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 23:02:48 +02:00
Meta Vens
15f96c408c Heimdall sign-in: attempt cookies live in a ring of two fixed slots, so the Cookie header is bounded by construction; pin the completion form's id
Start takes a free slot or overwrites the one expiring first; parallel starts carry the same request
cookies, choose the same slot and overwrite each other. Complete finds its attempt by scanning the
slots for the posted nonce; an overwritten attempt finds none and is refused like any other. The
per-request counting sweep, its byte budget and the nonce-named cookie are deleted.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 22:25:24 +02:00
Meta Vens
4a28cea1f9 Heimdall sign-in: Start sweeps dead and oldest attempt cookies to keep the Cookie header bounded; pin the complete page's real form
Start deletes request attempt cookies that are no live attempt, then the live ones expiring first until the new attempt and the survivors fit 5120 bytes, so the newest attempt always completes and the header stays under nginx's 8 KB. Behavioural tests drive a cookie jar past the bound and read the served page's form fields and action through the test host.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 20:41:41 +02:00
Meta Vens
34f5dfa6f1 Heimdall attempt cookie: a deletion carries only the Path
HttpOnly, Secure and SameSite on a deletion decide nothing (a cookie is matched on name and path); Stryker found those two booleans surviving. The deletion is pinned by path, expiry and the absence of Max-Age.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:08:10 +02:00
Meta Vens
d695674359 Heimdall sign-in: redact the app secret from client logging, expire the attempt cookie at its path, bound the return path
- The HeimdallAuthService client redacts x-heimdall-app-secret from HTTP client logging at every level; a Trace canary test pins it.
- The attempt cookie's deletion carries its Path and no Max-Age (which outranks the 1970 expiry); pinned on success and refusal paths.
- LocalReturnPath is at most 256 characters, else /App, so the attempt cookie stays far below the browser limit; pinned at the bound and with the heaviest-escaping path.
- A set BaseUrl with a blank AppSlug is refused at Start before any Heimdall call; behavioural test.

Builds untested until the verification job at the end of this batch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 08:58:19 +02:00
Meta Vens
5626532571 Heimdall sign-in: the start result carries the cookie name; fix a test string literal
The commit before this one did not compile (a split string literal in the tests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:41:36 +02:00
Meta Vens
bad2117ea8 Heimdall sign-in: the attempt is a cookie, and a code the browser presents is redeemed at Heimdall
Start puts a per-attempt nonce in the returnTo it sends Heimdall and keeps the attempt (provider,
member flag, local return path, expiry) in a Data Protection cookie named for that nonce. Heimdall's
completion page sends the browser to /auth/heimdall/complete with a code in the URL fragment; the
page posts it back, Bifrost redeems it at Heimdall, requires the redeemed returnTo to name the
cookie's nonce, verifies the access token for the redeemed account, and signs that browser in.

Deleted: the callback route, the wait route, the server-side attempt store, the pending state and
the backend_callback handoff. Added: LocalReturnPath for return targets, Heimdall:AppSecret sent as
x-heimdall-app-secret (member starts are refused without it), a short timeout on the key fetch, and
a key lookup that does not hold the fetch gate for cached keys. Refusals at the completion route all
answer one redirect and log a fixed reason.

Tests are rewritten against a fake Heimdall with start and redeem routes. Not compile-checked by this
commit alone; later commits record the verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:40:47 +02:00
Meta Vens
1ab67da47b Heimdall sign-in tests: pin each refusal rule by its reason; token dates cannot overflow
The forgery and wrong-claim tests now assert which rule refused, start failures,
blank attempt ids, capability shapes and date extremes are covered. A token whose
exp is the largest representable date no longer overflows the clock-skew sum, and
the unreachable blank-return-url fallback is removed (Program defaults it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:26:48 +02:00
Meta Vens
f3b8acbbc5 Heimdall sign-in: accept only a Heimdall-signed token for an attempt Start issued, in the browser that started it
The callback verifies the EdDSA access token against Heimdall's JWKS and takes the
account, display name and member access from its claims; attempts expire, are
bounded, are completed only if issued, and are redeemed once with a per-attempt
binding cookie. Not yet built or tested; the first verification run follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 02:54:13 +02:00
Meta Vens
23c02be2a6 Add native Bifrost identity registration 2026-06-22 15:07:22 +01:00
Meta Vens
861dff3ed7 Add Stripe patronage checkout integration 2026-06-22 14:08:35 +01:00
Meta Vens
53b8f9a9ab Add Heimdall Discord Patreon access 2026-06-08 21:04:51 +01:00