feat(backend): self-registration for Gemeinde Verantwortliche

New onboarding/ module. A prospective Verantwortliche/r signs in with their
Konfi-Castle-ID (Authentik), looks up a KC by invite code, picks an existing
Gemeinde, and registers:

- GET  /api/onboarding/kc/:inviteCode  -> KC name + its Gemeinden (public;
  the invite code is the shared secret)
- POST /api/onboarding/verantwortliche -> verifies the raw Authentik bearer
  token's claims (no local Membership required yet via new
  TokenVerificationService.verifyAuthentikClaims), JIT-provisions the local
  User, and creates a Membership with status PENDING. Idempotent per
  (user, kc, gemeinde).
- GET  /api/onboarding/requests?kcId=            (LT) list pending
- POST /api/onboarding/requests/:id/approve|reject (LT) approve flips to
  ACTIVE, reject deletes.

Schema: Membership gains status (enum MembershipStatus { ACTIVE, PENDING },
default ACTIVE). AuthentikStrategy / TokenVerificationService / TeamAuthService
now load only ACTIVE memberships, so a pending request grants nothing until
approved. Membership create/update/delete flow through the sync log.

Tests: onboarding.service.spec.ts (14 cases); npm test green at 46.
Docs (plan + backend README) updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 08:01:19 +02:00
co-authored by Claude Sonnet 5
parent d48c07b0e4
commit 6ed5aa2c76
11 changed files with 524 additions and 13 deletions
+2
View File
@@ -7,6 +7,7 @@ import { AuthModule } from './auth/auth.module';
import { KcModule } from './kc/kc.module';
import { GemeindeModule } from './gemeinde/gemeinde.module';
import { TeamerModule } from './teamer/teamer.module';
import { OnboardingModule } from './onboarding/onboarding.module';
import { WahlModule } from './wahl/wahl.module';
import { FilesModule } from './files/files.module';
import { ChatModule } from './chat/chat.module';
@@ -27,6 +28,7 @@ import { SyncModule } from './sync/sync.module';
KcModule,
GemeindeModule,
TeamerModule,
OnboardingModule,
WahlModule,
FilesModule,
ChatModule,