[Epic][Architecture] Anonymous-vs-authenticated surface — explicit public/protected split #417

Closed
opened 2026-07-05 20:24:45 +00:00 by spikerj · 2 comments
Owner

Context: From the anonymous-vs-authenticated architecture discussion. Design intent: an unregistered visitor can explore the site much like a member, without anonymous access to anything requiring a user or exposing other users' data. The house pattern already exists (api-conventions.md #312: controller [Authorize] + [AllowAnonymous] on public actions; TrailsController / PreRegistrationHub as examples) but is applied inconsistently — and [AllowAnonymous] alone controls access, not response shape.

Three-layer model to implement:

  1. Transport — [Authorize] by default + explicit [AllowAnonymous] on the explore surface + a FallbackPolicy safety net so omissions fail closed (CR-2, this batch).
  2. Contract — public vs enriched DTO projections (dual queries) for anything touching user data, so a guest response physically cannot carry user fields; a single anonymous endpoint is fine for pure public content. Also resolves "persistence entities leaked across the API boundary."
  3. Application — optional MediatR authorization behavior keyed on a marker (IPublicRequest / default-deny), enforced regardless of dispatcher. Note the caching trap: an auth-aware cache key must include identity/anonymity, and the pipeline order is load-bearing.

Work items:

  • Per-controller classification: protect / explore / decide
  • Characterize-first: make the anonymous Playwright walk comprehensive (#314 / #307) as the regression net BEFORE tightening
  • SignalR: a curated public read-model for the "our hardware" showcase instead of exposing the raw telemetry hubs (#401)
  • Children: CR-2 (transport layer), Trails anonymous-delete (specific instance)

Acceptance criteria:

  • Every controller/hub explicitly classified; the anonymous surface is greppable ([AllowAnonymous])
  • Guest responses use public projections; no member data reachable anonymously
  • Anonymous walk green throughout

Effort: L (epic) · Related: #401, #314, #307, #312, #298.


Child issues (per-repo, auto-close on merge)

This epic is complete when every box below is checked. Each child lives in the
repo that holds its fix, so fixes #N in a PR closes it automatically.

Backend

Checklist generated by the umbrella-tracker migration, 2026-08-07 — Opus 5 Agent

**Context:** From the anonymous-vs-authenticated architecture discussion. Design intent: an unregistered visitor can *explore* the site much like a member, without anonymous access to anything requiring a user or exposing other users' data. The house pattern already exists (`api-conventions.md` #312: controller `[Authorize]` + `[AllowAnonymous]` on public actions; `TrailsController` / `PreRegistrationHub` as examples) but is applied inconsistently — and `[AllowAnonymous]` alone controls *access*, not *response shape*. **Three-layer model to implement:** 1. **Transport** — `[Authorize]` by default + explicit `[AllowAnonymous]` on the explore surface + a `FallbackPolicy` safety net so omissions fail closed (CR-2, this batch). 2. **Contract** — public vs enriched DTO projections (dual queries) for anything touching user data, so a guest response *physically* cannot carry user fields; a single anonymous endpoint is fine for pure public content. Also resolves "persistence entities leaked across the API boundary." 3. **Application** — optional MediatR authorization behavior keyed on a marker (`IPublicRequest` / default-deny), enforced regardless of dispatcher. Note the caching trap: an auth-aware cache key must include identity/anonymity, and the pipeline order is load-bearing. **Work items:** - Per-controller classification: protect / explore / decide - Characterize-first: make the anonymous Playwright walk comprehensive (#314 / #307) as the regression net BEFORE tightening - SignalR: a curated public read-model for the "our hardware" showcase instead of exposing the raw telemetry hubs (#401) - Children: CR-2 (transport layer), Trails anonymous-delete (specific instance) **Acceptance criteria:** - Every controller/hub explicitly classified; the anonymous surface is greppable (`[AllowAnonymous]`) - Guest responses use public projections; no member data reachable anonymously - Anonymous walk green throughout **Effort:** L (epic) · Related: #401, #314, #307, #312, #298. <!-- BEGIN MIGRATED-CHILDREN --> --- ## Child issues (per-repo, auto-close on merge) This epic is complete when every box below is checked. Each child lives in the repo that holds its fix, so `fixes #N` in a PR closes it automatically. **Backend** - [ ] spikerj/spikersoft-backend#590 — [Security][Go-live] Add authorization FallbackPolicy so un-annotated API endpoints fail closed <sub>(was #415)</sub> <sub>Checklist generated by the umbrella-tracker migration, 2026-08-07 — Opus 5 Agent</sub> <!-- END MIGRATED-CHILDREN -->
spikerj added the agentic label 2026-07-05 20:24:45 +00:00
Author
Owner

Children (this batch): CR-2 transport layer #415, Trails anonymous-delete #416. Related: #401 (SignalR hubs), #314 / #307 (anonymous Playwright walk — the regression net), #312, #298.

**Children (this batch):** CR-2 transport layer #415, Trails anonymous-delete #416. **Related:** #401 (SignalR hubs), #314 / #307 (anonymous Playwright walk — the regression net), #312, #298.
spikerj added the epic label 2026-08-07 13:43:01 +00:00
Author
Owner

Dissolved into per-repo issues as part of the umbrella-tracker breakup.

This epic could never auto-close from a merge, because Gitea only resolves
fixes #N within a single repo. Its work now lives where the code is:

Backend

The unit is tracked by the shared [Auth surface] title prefix and by sibling
cross-links in each issue body. Closing here — the umbrella tracker is being
emptied.

— Opus 5 Agent

Dissolved into per-repo issues as part of the umbrella-tracker breakup. This epic could never auto-close from a merge, because Gitea only resolves `fixes #N` within a single repo. Its work now lives where the code is: **Backend** - spikerj/spikersoft-backend#590 — [Security][Go-live] Add authorization FallbackPolicy so un-annotated API endpoints fail closed The unit is tracked by the shared `[Auth surface]` title prefix and by sibling cross-links in each issue body. Closing here — the umbrella tracker is being emptied. — Opus 5 Agent
Sign in to join this conversation.