- 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>
The key lookup's second cache check after taking the gate changed nothing the refetch throttle did not
already decide, so it is removed. Mutation testing of the diff found the three behaviours above unpinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
The verifier was registered as a typed client, which is transient, so each request
would have started with an empty key cache. It is now a singleton that takes its
HTTP client from the factory per fetch. Test hosts share one in-memory database
root so EF keeps a single service provider.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>