feat(backend): JIT-provision the local User on first Authentik login
AuthentikStrategy no longer rejects a valid token whose user has no local row — it creates the User from the token claims (given_name/family_name/ email) via the new shared resolveOrProvisionAuthentikUser helper, which is race-safe (P2002 -> re-read) and captures the User to the sync log. The WS token path (TokenVerificationService.verifyAuthentik) and OnboardingService now use the same helper, removing three copies of the lookup/create logic. A provisioned user still has no Membership and therefore no rights: LT role assignment from Authentik groups is the remaining gap; Verantwortliche go through the onboarding approval flow. Tests: provision-user.spec.ts (existing/new/race/rethrow); npm test green at 51. Docs updated. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,12 +20,16 @@ client's host - no separate web server is needed.
|
||||
|
||||
## Auth model
|
||||
|
||||
- Leitungsteam and Gemeinde Verantwortliche are provisioned in Authentik
|
||||
(the "Konfi-Castle-ID"); this API acts as an OIDC **resource server**,
|
||||
verifying access tokens against Authentik's JWKS (`AuthentikStrategy`) and
|
||||
then resolving local `Membership` rows to determine role + KC/Gemeinde
|
||||
scope. Clients perform the actual Authorization Code + PKCE flow against
|
||||
Authentik directly.
|
||||
- Leitungsteam and Gemeinde Verantwortliche sign in with Authentik (the
|
||||
"Konfi-Castle-ID"); this API acts as an OIDC **resource server**, verifying
|
||||
access tokens against Authentik's JWKS (`AuthentikStrategy`). The local
|
||||
`User` is provisioned just-in-time on first login from the token claims
|
||||
(`resolveOrProvisionAuthentikUser`); role + KC/Gemeinde scope then come
|
||||
from local `Membership` rows (only `status = ACTIVE` ones count). A freshly
|
||||
provisioned user has no membership and thus no rights until one is granted
|
||||
(LT: manually for now; Verantwortliche: the `onboarding/` approval flow).
|
||||
Clients perform the Authorization Code + PKCE flow against Authentik
|
||||
directly.
|
||||
- Gemeinde Teamer are **local accounts** (no Authentik): a `User` row with a
|
||||
`passwordHash` and `kcId` set, `authentikSub` left null. A Gemeinde
|
||||
Verantwortliche/r creates them directly or via a `TeamerInvite`
|
||||
@@ -105,8 +109,9 @@ client's host - no separate web server is needed.
|
||||
Leitungsteam roles are global across all KCs).
|
||||
|
||||
All planned backend phases are implemented. `npm test` runs Jest unit tests
|
||||
(`ZuteilungService`, `TeamAuthService`, `TeamerService`, `OnboardingService`;
|
||||
Prisma mocked). Remaining work: the Flutter clients (see repo root README),
|
||||
Authentik JIT provisioning for LT (Verantwortliche already self-provision via
|
||||
`onboarding/`), invite email delivery, and the first real Prisma migration
|
||||
(only `schema.prisma` exists so far).
|
||||
(`ZuteilungService`, `TeamAuthService`, `TeamerService`, `OnboardingService`,
|
||||
`resolveOrProvisionAuthentikUser`; Prisma mocked). Remaining work: the
|
||||
Flutter clients (see repo root README), deriving the LT `Membership` from
|
||||
Authentik group claims (the `User` is provisioned, the role is not), invite
|
||||
email delivery, and the first real Prisma migration (only `schema.prisma`
|
||||
exists so far).
|
||||
|
||||
Reference in New Issue
Block a user