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>
36 lines
1.1 KiB
TypeScript
36 lines
1.1 KiB
TypeScript
import { Module } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { JwtModule } from '@nestjs/jwt';
|
|
import { PassportModule } from '@nestjs/passport';
|
|
import { AuthController } from './auth.controller';
|
|
import { GuestAuthService } from './guest-auth.service';
|
|
import { TeamAuthService } from './team-auth.service';
|
|
import { AuthentikStrategy } from './authentik.strategy';
|
|
import { GuestJwtStrategy } from './guest-jwt.strategy';
|
|
import { TeamJwtStrategy } from './team-jwt.strategy';
|
|
import { TokenVerificationService } from './token-verification.service';
|
|
|
|
@Module({
|
|
imports: [
|
|
PassportModule,
|
|
JwtModule.registerAsync({
|
|
inject: [ConfigService],
|
|
useFactory: (config: ConfigService) => ({
|
|
secret: config.getOrThrow<string>('GUEST_JWT_SECRET'),
|
|
signOptions: { expiresIn: '12h' },
|
|
}),
|
|
}),
|
|
],
|
|
controllers: [AuthController],
|
|
providers: [
|
|
GuestAuthService,
|
|
TeamAuthService,
|
|
AuthentikStrategy,
|
|
GuestJwtStrategy,
|
|
TeamJwtStrategy,
|
|
TokenVerificationService,
|
|
],
|
|
exports: [TokenVerificationService, TeamAuthService],
|
|
})
|
|
export class AuthModule {}
|