bug: SSR sends admin API key for unauthenticated visitors — private content exposed #38

Closed
opened 2026-03-16 23:49:50 +00:00 by forgejo_admin · 0 comments

Lineage

plan-pal-e-docs → Phase F14 (phase-pal-e-docs-f14-public-readiness)

Repo

forgejo_admin/pal-e-app

User Story

As a public visitor browsing pal-e-app without a Keycloak session
I want to only see public notes, projects, and boards
So that private content (journals, personal projects, internal boards) is not visible to me

As Lucas (admin with Keycloak session)
I want to see all content including private notes
So that my admin experience is unchanged

Context

Phase F6 (PR #29) added X-PaleDocs-Token to apiFetch() via the PAL_E_DOCS_API_KEY env var. The bug: this header is sent on EVERY SSR request, even when the visitor has no Keycloak session. The pal-e-docs API correctly filters private content for unauthenticated requests, but the frontend always authenticates as admin.

Current flow (broken):

Anonymous visitor → SvelteKit SSR → API (with admin token) → ALL notes rendered

Expected flow:

Anonymous visitor → SvelteKit SSR → API (no token) → public notes only
Admin (Lucas)     → SvelteKit SSR → API (with token) → all notes

Evidence: after signing out, the dashboard still shows private note titles ("The Chinese Room..."), private projects ("Private", "Remember"), and private boards ("Private Board", "Remember") with full content.

File Targets

Files the agent should modify:

  • src/lib/api.ts (or wherever apiFetch is defined) — conditionally include X-PaleDocs-Token only when user has active Keycloak session
  • src/routes/+layout.server.ts (or equivalent) — pass session/auth state to child routes
  • src/routes/+page.server.ts — use session state to determine whether to send auth token in API calls
  • src/routes/signin/+page.svelte — replace Keycloak redirect with "Contact Lucas for access" page + portfolio link
  • tests/ or e2e/ — E2E test for anonymous visitor content filtering

Files the agent should reference:

  • src/hooks.server.ts — existing auth/session handling
  • Keycloak session API — how to check if user is authenticated server-side

Acceptance Criteria

  • When I visit pal-e-app without a Keycloak session, then I see only public notes in "Recently Updated"
  • When I visit without session, then "Private" and "Remember" are NOT in the projects list
  • When I visit without session, then "Private Board" and "Remember" are NOT in board progress
  • When I visit without session, then no private note titles are visible anywhere
  • When I sign in as admin (Lucas), then I see ALL content including private notes/projects/boards
  • When I click "Sign in", then I see a "Contact Lucas for access" page with a link to the portfolio contact form (URL: portfolio.tail5b443a.ts.net)
  • The "Contact Lucas" page does NOT redirect to Keycloak — it is a static informational page

Test Expectations

  • E2E test: load home page without auth → verify no elements with "PRIVATE" badge, no "Private" project, no "Remember" project
  • E2E test: load signin page → verify "Contact Lucas" messaging is present
  • Manual verification: sign in as admin → all content visible
  • Run command: npx playwright test or equivalent

Constraints

  • Do NOT remove Keycloak integration — admin login must still work. The apiFetch should check for session, not remove the auth capability
  • The "Sign in" link in the nav should still exist but route to the "Contact Lucas" page
  • Admin login flow: add a small "Admin login" link (subtle, footer-level) that goes to the actual Keycloak auth flow, or use a query param like /signin?admin=true
  • Preserve the lock icon on private notes for authenticated users — just don't render private notes at all for anonymous users

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • project-pal-e-docs — project this affects
  • phase-pal-e-docs-private-notes — Phase F6 (PR #29), the original implementation this fixes
### Lineage `plan-pal-e-docs` → Phase F14 (`phase-pal-e-docs-f14-public-readiness`) ### Repo `forgejo_admin/pal-e-app` ### User Story As a public visitor browsing pal-e-app without a Keycloak session I want to only see public notes, projects, and boards So that private content (journals, personal projects, internal boards) is not visible to me As Lucas (admin with Keycloak session) I want to see all content including private notes So that my admin experience is unchanged ### Context Phase F6 (PR #29) added `X-PaleDocs-Token` to `apiFetch()` via the `PAL_E_DOCS_API_KEY` env var. The bug: this header is sent on EVERY SSR request, even when the visitor has no Keycloak session. The pal-e-docs API correctly filters private content for unauthenticated requests, but the frontend always authenticates as admin. Current flow (broken): ``` Anonymous visitor → SvelteKit SSR → API (with admin token) → ALL notes rendered ``` Expected flow: ``` Anonymous visitor → SvelteKit SSR → API (no token) → public notes only Admin (Lucas) → SvelteKit SSR → API (with token) → all notes ``` Evidence: after signing out, the dashboard still shows private note titles ("The Chinese Room..."), private projects ("Private", "Remember"), and private boards ("Private Board", "Remember") with full content. ### File Targets Files the agent should modify: - `src/lib/api.ts` (or wherever `apiFetch` is defined) — conditionally include `X-PaleDocs-Token` only when user has active Keycloak session - `src/routes/+layout.server.ts` (or equivalent) — pass session/auth state to child routes - `src/routes/+page.server.ts` — use session state to determine whether to send auth token in API calls - `src/routes/signin/+page.svelte` — replace Keycloak redirect with "Contact Lucas for access" page + portfolio link - `tests/` or `e2e/` — E2E test for anonymous visitor content filtering Files the agent should reference: - `src/hooks.server.ts` — existing auth/session handling - Keycloak session API — how to check if user is authenticated server-side ### Acceptance Criteria - [ ] When I visit pal-e-app without a Keycloak session, then I see only public notes in "Recently Updated" - [ ] When I visit without session, then "Private" and "Remember" are NOT in the projects list - [ ] When I visit without session, then "Private Board" and "Remember" are NOT in board progress - [ ] When I visit without session, then no private note titles are visible anywhere - [ ] When I sign in as admin (Lucas), then I see ALL content including private notes/projects/boards - [ ] When I click "Sign in", then I see a "Contact Lucas for access" page with a link to the portfolio contact form (URL: `portfolio.tail5b443a.ts.net`) - [ ] The "Contact Lucas" page does NOT redirect to Keycloak — it is a static informational page ### Test Expectations - [ ] E2E test: load home page without auth → verify no elements with "PRIVATE" badge, no "Private" project, no "Remember" project - [ ] E2E test: load signin page → verify "Contact Lucas" messaging is present - [ ] Manual verification: sign in as admin → all content visible - Run command: `npx playwright test` or equivalent ### Constraints - Do NOT remove Keycloak integration — admin login must still work. The `apiFetch` should check for session, not remove the auth capability - The "Sign in" link in the nav should still exist but route to the "Contact Lucas" page - Admin login flow: add a small "Admin login" link (subtle, footer-level) that goes to the actual Keycloak auth flow, or use a query param like `/signin?admin=true` - Preserve the lock icon on private notes for authenticated users — just don't render private notes at all for anonymous users ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `project-pal-e-docs` — project this affects - `phase-pal-e-docs-private-notes` — Phase F6 (PR #29), the original implementation this fixes
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/pal-e-docs-app#38
No description provided.