linusandClaude Sonnet 5 df11d8492d feat: Authentik OIDC login (PKCE) + Leitungsteam admin screens
Backend:
- AuthentikStrategy / TokenVerificationService: normalise the issuer's
  trailing slash and accept both `iss` spellings (Authentik's discovery
  issuer and token `iss` carry a trailing slash; the JWKS URL must not
  double it). Wire the real konfi-castle issuer into .env.example.
- team token path now goes through toAuthenticatedUser too, so a local
  account flagged isLeitungsteam gets the synthetic global LT membership
  regardless of token kind.
- LT-admin controllers (kc, gemeinde, onboarding, sync, teamer) accept
  ['authentik','team'] so such an account can use them. RolesGuard still
  enforces the actual LT/role check.
- app.module serves the Flutter web build from client/app/build/web (SPA
  fallback covers the OIDC redirect path /v1/auth/callback), falling back
  to the interim client/web/ if it isn't built.

Client (client/app/):
- oidc.dart: Authorization-Code + PKCE against Authentik (discovery, S256
  challenge, state, token exchange, refresh). Browser bits (sessionStorage,
  redirect, URL) behind a conditional import so `flutter test` still
  compiles on the VM.
- AppState handles the ?code= callback on bootstrap, stores access +
  refresh, refreshes an expired token on restart.
- Login screen: "Mit Konfi-Castle-ID anmelden" button (Leitungsteam /
  Verantwortliche) alongside the local Teamer password form.
- admin_screen.dart: LT-only "Verwaltung" — list/create KCs, per KC the
  Gemeinden (list/create) and pending Verantwortlichen requests
  (approve/reject). Verified end to end against local Postgres with an
  isLeitungsteam account (create KC/Gemeinde, list + approve a request).

flutter analyze/test/build web green; backend npm test 56.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 09:24:38 +02:00
2026-09-09 08:30:18 +00:00

KC-APP

Multi-tenant event, election and communication platform for Konfi-Castle events (KCs), replacing the WordPress plugin "Workshop-Wahlen". See plan-kcAppMultiTenantPlatform.prompt.md for the full architecture and phased roadmap.

Structure

  • backend/ — NestJS API (Prisma/PostgreSQL, Authentik OIDC as resource server, guest/Konfi local accounts, roles/permissions foundation, file sharing, chat, local/cloud sync). See backend/README.md for setup. Also serves the web client (see below) directly, so it's the single entry point for the web experience.
  • client/app/ — the Flutter client (single codebase; web target enabled, mobile/desktop can be added later). Login (guest / local Teamer / invite redemption), role-aware home, guest Workshop-Wahl, file list, read-only chat. See client/app/README.md.
  • client/web/ — minimal dependency-free HTML/CSS/JS placeholder web client, served by the backend at /. Superseded by the Flutter web build; kept for now as a zero-dependency fallback.

Status

Phase 0/1 foundation implemented: monorepo skeleton, Prisma data model (Kc, Gemeinde, User, Membership, GuestAccount, Wahl/Workshop/Teilnehmer/Zuteilung, File, Chat), Authentik JWT resource-server strategy, guest invite-code login, Role-based guard scoped per KC.

Phase 2 (Workshop-Wahl engine) implemented: Wahl/Workshop administration, guest Teilnehmer submission, Force-Zuteilung overrides, and the assignment algorithm ported from the WP plugin's kc_run_zuteilung (force-assignments → wish rounds 1-3 → random fill → consolidation of underfilled workshops), plus CSV export.

Phase 3 (Dateifreigabe) implemented: Leitungsteam-only upload tagged with a visibility tier (alle / alle außer Konfis / nur LT), list/download for Authentik or guest callers filtered by their allowed tiers, storage behind a provider abstraction defaulting to Nextcloud/WebDAV (S3-compatible storage as an alternative via STORAGE_PROVIDER=s3).

Phase 5 (Kommunikation) implemented: Gemeinde-Gruppenchat, 1:1-DMs, LT- kanalübergreifende Kanäle, Broadcast (read-only für Konfis); channel/history via REST, real-time send/receive via a raw WebSocket gateway authenticated with the same Authentik/guest tokens as the REST API.

Phase 6 (Hybrid Lokal/Cloud-Server & Sync) implemented: an append-only replication log (SyncLogEntry) captured by every feature service after its writes; the local (on-site) server periodically pushes/pulls against the cloud server's /sync/ingest + /sync/export endpoints (shared-secret authenticated, not user auth). No conflict resolution needed by design - the local server is the sole source of truth while an event is live.

Since then: local (non-Authentik) Gemeinde Teamer accounts + invites (teamer/, auth/team-login), Gemeinde CRUD (gemeinde/), Gemeinde Verantwortliche self-registration with LT approval (onboarding/), JIT User provisioning on first Authentik login, LEITUNGSTEAM derived from the Authentik groups claim, and an email module (mail/, log-only by default, SMTP opt-in) that sends personal Teamer invites. First Prisma migration is in (backend/prisma/migrations/); the backend has been run end to end against a local PostgreSQL 16. npm test covers the assignment algorithm and the new auth/onboarding services (56 tests).

Phase 7 (Flutter client) started: client/app/ is a single Flutter codebase with the web target enabled — login (guest / local Teamer / invite redemption), role-aware home, guest Workshop-Wahl, file list, read-only chat. flutter build web and flutter test pass. Mobile/desktop targets, the Authentik Authorization-Code flow, WebSocket chat send, and LT admin screens are still to come.

The backend also serves the interim client/web/ placeholder at / (API under /api). Running end to end still needs a real Authentik instance and Nextcloud/S3 credentials (see backend/.env.example).

S
Description
No description provided
Readme GPL-3.0
585 KiB
Languages
Dart 78.3%
JavaScript 9.9%
HTML 7.5%
CSS 4.3%