Quick 260927-q23 (CR-01), unit coverage that runs under -short.
- bouncer: TestRefreshAudienceForSubject covers active, pre/post cutoff,
missing, nil provider, non-numeric sub, provider error, and proves
token-only refusals never reach the provider
- bouncer: TestJWTGuardTokensValidAfter pins the unchanged "User not found"
message and errors.Is(err, ErrSubjectRejected)
- cabana: TestPhase10Coverage subtest pins cookie expiry on subject
refusals, no cookies over Bearer or on a provider error, and the
post-cutoff success path
Fixes review finding CR-01 (quick 260927-q23): POST {prefix}/api/v1/auth/refresh
minted a new token without loading the admin, so a session kept alive by the
SPA's refresh-on-401 survived admin:reset-password, deactivation and deletion.
This broke Phase 9 truth T-09-04.
- bouncer: extract the JWT guard's subject lookup into subjectPrincipal and
issuedBeforeCutoff (same order and messages), add ErrSubjectRejected
- bouncer: add RefreshAudienceFor, which runs the guard's subject checks
after the token-only checks and before minting; Refresh and
RefreshAudience are unchanged (nil hook)
- cabana: share one lazyBackendUsers provider between the backend guard and
refresh; a cookie refresh refused for its subject expires summer_admin
- test: TestAdminRefreshRevocation (Postgres, real admin:reset-password)
- backend.uri prefix (default /backend) mounts the admin API at {prefix}/api/v1
and the embedded SPA shell at {prefix} with an api/ JSON 404 fallback
- cookie transport: an X-Requested-With login sets the HttpOnly summer_admin
cookie and returns no token; the backend guard reads the cookie after Bearer
- CSRF wrapper refuses cookie-only POST/PUT/DELETE without X-Requested-With
- boardwalk package embeds boardwalk/dist, rewrites index.html once per prefix
and sets cache and security headers
- framework admin OpenAPI pipeline (swag, swagger2openapi, openapi-typescript)
with prefix-relative paths and typed envelopes for the tracer routes
- admin/ Vite SPA: login, plugin rail, section panel and read-only list
through the openapi-fetch client typed by the generated schema
- Same-secret backend token is still accepted by the frontend guard
- Permission denial must not invoke the schema or database callback
- Admin error bodies must not echo secrets or raw tokens
- Audience-aware mint, verify, refresh, and backend guard keep frontend tokens compatible
- Cabana mounts raw admin login, list schema, and record list behind admin.jwt.secret
- Framework migration seeds Winter backend users and developer/publisher roles
A minted token refreshes once, then the old jti is blacklisted. Memory and Postgres blacklists take concurrent Add and IsBlacklisted calls.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add Post/Put/Patch/Delete on pact.Router and surf Router/Group
- Resolve name:param middleware via RegisterMiddlewareFactory
- Add bouncer.Registry with Guard, CredentialGuard, UnauthorizedWriter
- Re-express jwt as NewJWTGuard without changing Middleware bodies
Named middleware resolves at boot, HS256 tokens are pinned with required
exp/sub, and both binaries expose a signal-aware serve command.
Co-authored-by: Cursor <cursoragent@cursor.com>