feat: Gast-Gating fürs Co-Branding Studio (nur freigeschaltete Partner) #3

Merged
andre merged 2 commits from a3/guest-gating into main 2026-09-27 00:03:10 +02:00
Owner

Zusammenfassung

Antwort auf: "Neuer Partner soll nicht sofort Zugriff auf alles haben, sondern erst nach Gespräch mit André freigeschaltet werden."

Befund: Der Onboarding/Freigabe-Fluss war bereits korrekt gebaut (status='pending' ohne Session-Möglichkeit, bis approvePartner() explizit läuft, PR #1/#2). Die eigentliche Lücke lag im Frontend: Co-Branding Studio, DAM, Aktivierungs-Kit und Cockpit waren für jeden Website-Besucher frei nutzbar, unabhängig vom Freigabe-Status — der Studio-Partnername ist ein freies Textfeld, keine Bindung an einen echten, freigeschalteten Partner-Datensatz. Das widerspricht der Rollenmatrix aus dem Pflichtenheft (Kap. 5), die Co-Branding-Generator & Downloads explizit auf freigeschaltete Partner beschränkt.

Änderungen

  • GET /api/session/me: meldet, ob ein gültiges Session-Cookie vorliegt (aus /auth/verify, PR #2) und zu welchem Partner.
  • POST /api/session/logout: löscht das Session-Cookie.
  • Frontend sperrt jetzt die Tabs Studio, DAM, Aktivierungs-Kit, Cockpit für nicht eingeloggte Besucher (Schloss-Icon, Klick → Hinweis + Sprung zu "Bewerben"). Öffentlich bleiben Partner-Modell & Stufen, Reviere-Status, Bewerben — das sind die Marketing-/Pitch-Tabs, die auch unangemeldete Interessenten überzeugen sollen.
  • Header zeigt "Eingeloggt als <Partner>" mit Abmelden-Link, sobald eine gültige Session vorliegt.

Bewusst nicht Teil dieses PRs

  • Die WP/ks-crm-Checkbox-Integration (damit André direkt aus seinem CRM-Profil heraus freischalten kann, statt curl/API) ist ein Cross-Repo-Task in kurven-schule-neu-wp — folgt separat.
  • Trainer bekommen nach Freischaltung über /trainer-werden/ noch keinen eigenen Portal-Zugang zum regionalen Partner-Netzwerk (Händler/Hotels in ihrer Region) — ebenfalls separater Folge-PR (Backlog).

Hinweis zur UI-Verifikation

Die Gating-Logik ist per Unit-Test auf Funktions-/Endpoint-Ebene abgesichert (34/34 grün), aber nicht in einem echten Browser durchgeklickt — das sollte vor/nach dem Merge einmal manuell auf https://partner.kurvenschule.cloud nachgeholt werden (Studio-Tab als Gast anklicken → Sperr-Hinweis, dann Magic-Link-Login → alles frei).

Test plan

  • npm test — 34/34 grün (4 neue Tests: /api/session/me mit/ohne Cookie, /api/session/logout, Gating-Funktionen im Bundle)
  • Manueller Klicktest im Browser (siehe Hinweis oben)

🤖 Generated with Claude Code

## Zusammenfassung Antwort auf: "Neuer Partner soll nicht sofort Zugriff auf alles haben, sondern erst nach Gespräch mit André freigeschaltet werden." **Befund:** Der Onboarding/Freigabe-Fluss war bereits korrekt gebaut (`status='pending'` ohne Session-Möglichkeit, bis `approvePartner()` explizit läuft, PR #1/#2). Die eigentliche Lücke lag im **Frontend**: Co-Branding Studio, DAM, Aktivierungs-Kit und Cockpit waren für jeden Website-Besucher frei nutzbar, unabhängig vom Freigabe-Status — der Studio-Partnername ist ein freies Textfeld, keine Bindung an einen echten, freigeschalteten Partner-Datensatz. Das widerspricht der Rollenmatrix aus dem Pflichtenheft (Kap. 5), die Co-Branding-Generator & Downloads explizit auf freigeschaltete Partner beschränkt. ## Änderungen - `GET /api/session/me`: meldet, ob ein gültiges Session-Cookie vorliegt (aus `/auth/verify`, PR #2) und zu welchem Partner. - `POST /api/session/logout`: löscht das Session-Cookie. - Frontend sperrt jetzt die Tabs **Studio, DAM, Aktivierungs-Kit, Cockpit** für nicht eingeloggte Besucher (Schloss-Icon, Klick → Hinweis + Sprung zu "Bewerben"). Öffentlich bleiben **Partner-Modell & Stufen**, **Reviere-Status**, **Bewerben** — das sind die Marketing-/Pitch-Tabs, die auch unangemeldete Interessenten überzeugen sollen. - Header zeigt "Eingeloggt als `<Partner>`" mit Abmelden-Link, sobald eine gültige Session vorliegt. ## Bewusst nicht Teil dieses PRs - Die WP/`ks-crm`-Checkbox-Integration (damit André direkt aus seinem CRM-Profil heraus freischalten kann, statt curl/API) ist ein Cross-Repo-Task in `kurven-schule-neu-wp` — folgt separat. - Trainer bekommen nach Freischaltung über `/trainer-werden/` noch keinen eigenen Portal-Zugang zum regionalen Partner-Netzwerk (Händler/Hotels in ihrer Region) — ebenfalls separater Folge-PR (Backlog). ## Hinweis zur UI-Verifikation Die Gating-Logik ist per Unit-Test auf Funktions-/Endpoint-Ebene abgesichert (34/34 grün), aber **nicht** in einem echten Browser durchgeklickt — das sollte vor/nach dem Merge einmal manuell auf `https://partner.kurvenschule.cloud` nachgeholt werden (Studio-Tab als Gast anklicken → Sperr-Hinweis, dann Magic-Link-Login → alles frei). ## Test plan - [x] `npm test` — 34/34 grün (4 neue Tests: `/api/session/me` mit/ohne Cookie, `/api/session/logout`, Gating-Funktionen im Bundle) - [ ] Manueller Klicktest im Browser (siehe Hinweis oben) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: Gast-Gating fürs Co-Branding Studio (nur freigeschaltete Partner)
All checks were successful
Unit & Contract Tests / Node.js Contract Tests (pull_request) Successful in 7s
205185a1f7
Bisher konnte jeder Website-Besucher unabhängig vom Freigabe-Status
das komplette Co-Branding Studio, DAM, Aktivierungs-Kit und Cockpit
nutzen (freier Text-Input für Firmenname/Logo, keine Bindung an einen
echten Partner-Datensatz) — entgegen der Rollenmatrix aus dem
Pflichtenheft (Kap. 5), die diese Bereiche explizit auf freigeschaltete
Partner beschränkt.

- Neuer Endpoint GET /api/session/me: meldet, ob ein gültiges
  Session-Cookie vorliegt und zu welchem Partner es gehört.
- Neuer Endpoint POST /api/session/logout: löscht das Session-Cookie.
- Frontend sperrt Studio/DAM/Aktivierungs-Kit/Cockpit-Tabs für nicht
  eingeloggte Besucher (Schloss-Icon, Klick führt zur Bewerbung statt
  zum gesperrten Inhalt). Öffentlich bleiben: Partner-Modell & Stufen,
  Reviere-Status, Bewerben.
- Header zeigt "Eingeloggt als <Partner>" mit Abmelden-Link, sobald
  ein gültiges Session-Cookie vorliegt (z. B. nach Klick auf den
  Magic-Link aus der Freigabe-Mail).

Onboarding/Freigabe-Fluss war bereits korrekt (status='pending' ohne
Session-Möglichkeit bis approvePartner()) — die Lücke lag ausschließlich
im Frontend, das nie geprüft hat, ob überhaupt eine Freigabe vorliegt.

34/34 Tests grün (4 neue: /api/session/me mit/ohne Cookie,
/api/session/logout, Gating-Funktionen im Frontend-Bundle).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fix: Logout widerruft Session serverseitig, no-store für Session-Endpunkte
All checks were successful
Unit & Contract Tests / Node.js Contract Tests (pull_request) Successful in 8s
2be69ed461
DeepSeek-R1-Review von PR #3:
- POST /api/session/logout löschte bisher nur das Client-Cookie, die
  Session blieb in der DB aktiv. Ein bereits kopiertes/gestohlenes
  Cookie wäre nach "Abmelden" weiterhin gültig gewesen. Jetzt:
  revokeSessionByToken() widerruft genau die Session des vorgelegten
  Cookies serverseitig.
- GET /api/session/me und POST /api/session/logout setzen jetzt
  Cache-Control: no-store, private — verhindert, dass ein Shared
  Cache/Proxy Partnerdaten eines eingeloggten Nutzers an Dritte
  ausliefert.
- CSRF-Forced-Logout (Cross-Site-Form-POST auf /logout) bewusst nicht
  gehärtet: Auswirkung ist ein erzwungenes Abmelden ohne Datenzugriff,
  Kosten/Nutzen einer CSRF-Token-Infrastruktur für diesen einen
  zustandsändernden-aber-nicht-schädigenden Endpoint stehen aktuell
  nicht im Verhältnis — als Backlog-Punkt vermerkt.

34/34 Tests grün (Logout-Test erweitert: Session ist nach Logout auch
serverseitig ungültig, nicht nur das Cookie gelöscht).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andre merged commit 663f6d6658 into main 2026-09-27 00:03:10 +02:00
andre deleted branch a3/guest-gating 2026-09-27 00:03:10 +02:00
Sign in to join this conversation.
No reviewers
No labels
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
andre/kurven-schule-partner!3
No description provided.