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

Open
opened 2026-07-05 20:24:45 +00:00 by spikerj · 1 comment
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.

**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.
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.
Sign in to join this conversation.