Per the updated plan, Gemeinde Teamer are no longer Authentik-backed; they
are local accounts a Gemeinde Verantwortliche/r provisions per KC.
Schema:
- User.authentikSub now nullable; add passwordHash + kcId (cascade from Kc)
so one User model covers Authentik members and local Teamer.
- new TeamerInvite model: shareable group link (email null, maxUses null)
or personal invite (email pinned, single use), with expiry + revoke.
- sync log now also replicates User / Membership / TeamerInvite.
Auth:
- TeamAuthService: bcrypt password login (POST /auth/team-login) and invite
redemption (POST /auth/teamer/register) issuing a JWT signed with
TEAM_JWT_SECRET, payload typ:"team".
- TeamJwtStrategy (AuthGuard('team')) resolves it to the same
AuthenticatedUser shape as AuthentikStrategy.
- TokenVerificationService.verifyEither() also accepts team tokens (WS).
- files + chat read endpoints accept 'team' tokens; Teamer see non-Konfi
files and can use chat / start DMs.
Teamer admin (teamer/ module, under /gemeinde/:gemeindeId):
- POST/GET teamer, DELETE teamer/:userId
- POST/GET teamer-invites, DELETE teamer-invites/:inviteId
- LT may manage any Gemeinde; a Verantwortliche/r only their own
(checked in TeamerService, since RolesGuard only scopes by kcId).
Tests: TeamAuthService + TeamerService specs added (Prisma/Sync mocked),
npm test green at 32. Docs (plan + backend README) updated.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
29 lines
860 B
TypeScript
29 lines
860 B
TypeScript
import { Request } from 'express';
|
|
import { Role } from '../common/role.enum';
|
|
import { GuestJwtPayload } from './guest-auth.service';
|
|
|
|
export interface AuthenticatedMembership {
|
|
kcId: string;
|
|
gemeindeId: string | null;
|
|
role: Role;
|
|
}
|
|
|
|
/// Shape attached to req.user after validating an access token — by
|
|
/// AuthentikStrategy for Authentik-backed members, or by TeamJwtStrategy for
|
|
/// local Gemeinde Teamer (then `authentikSub` is null).
|
|
export interface AuthenticatedUser {
|
|
userId: string;
|
|
authentikSub: string | null;
|
|
email: string;
|
|
memberships: AuthenticatedMembership[];
|
|
}
|
|
|
|
export interface AuthenticatedRequest extends Request {
|
|
user?: AuthenticatedUser;
|
|
}
|
|
|
|
/// Shape attached to req.user by GuestJwtStrategy for guest/Konfi-authenticated routes.
|
|
export interface GuestAuthenticatedRequest extends Request {
|
|
user?: GuestJwtPayload;
|
|
}
|