fix: Rechteausweitung ueber Rollenzuweisung und weitere Auth-Haertung
Ein Sicherheitsaudit deckte auf, dass jede Rolle mit users:update sich selbst per PATCH /users/:id auf eine Admin-Rolle heraufstufen konnte, da roleId ohne eigene Berechtigung und ohne Selbstbezug-Pruefung akzeptiert wurde. Ausserdem lag ein fest im Code hinterlegtes Standard-Admin-Passwort vor, das bei fehlender SEED_ADMIN_PASSWORD-Env greift. - Neue Berechtigung users:assign-role, getrennt von users:create/update, fuer jede Rollenzuweisung erforderlich - Selbstaenderung der eigenen Rolle serverseitig blockiert (auch fuer Admins), ebenso Selbst-Reset der eigenen MFA ueber die Admin-Route - Seed erzeugt bei fehlender Env ein zufaelliges statt eines fixen Passworts - Wiederverwendung eines bereits rotierten Refresh-Tokens (Diebstahl- Indiz) widerruft nun alle Sessions des betroffenen Nutzers - Rate-Limit auf MFA-Enrollment-Bestaetigung ergaenzt Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
7bd2a3dc3a
commit
1b16b49913
7 files changed
+146
-7
No files matched your search
@@ -19,7 +19,10 @@ describe('AuthService', () => {
|
||||
};
|
||||
let jwtService: { sign: jest.Mock };
|
||||
let auditLog: { record: jest.Mock };
|
||||
let permissionResolver: { getPermissions: jest.Mock };
|
||||
let permissionResolver: {
|
||||
getPermissions: jest.Mock;
|
||||
invalidateUser: jest.Mock;
|
||||
};
|
||||
let mfaService: {
|
||||
signChallengeToken: jest.Mock;
|
||||
verifyChallengeToken: jest.Mock;
|
||||
@@ -52,6 +55,7 @@ describe('AuthService', () => {
|
||||
auditLog = { record: jest.fn().mockResolvedValue(undefined) };
|
||||
permissionResolver = {
|
||||
getPermissions: jest.fn().mockResolvedValue(new Set()),
|
||||
invalidateUser: jest.fn(),
|
||||
};
|
||||
mfaService = {
|
||||
signChallengeToken: jest.fn().mockReturnValue('mfa-challenge-token'),
|
||||
@@ -264,16 +268,26 @@ describe('AuthService', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when the refresh token is revoked', async () => {
|
||||
it('throws when the refresh token is revoked and revokes all other sessions for that user (reuse detection)', async () => {
|
||||
prisma.refreshToken.findUnique.mockResolvedValue({
|
||||
id: 'rt-1',
|
||||
userId: activeUser.id,
|
||||
revokedAt: new Date(),
|
||||
expiresAt: new Date(Date.now() + 1000),
|
||||
});
|
||||
prisma.refreshToken.updateMany.mockResolvedValue({ count: 2 });
|
||||
|
||||
await expect(authService.refresh('revoked-token')).rejects.toBeInstanceOf(
|
||||
UnauthorizedException,
|
||||
);
|
||||
expect(prisma.refreshToken.updateMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: { userId: activeUser.id, revokedAt: null },
|
||||
}),
|
||||
);
|
||||
expect(permissionResolver.invalidateUser).toHaveBeenCalledWith(
|
||||
activeUser.id,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when the refresh token is expired', async () => {
|
||||
|
||||
Reference in new issue
Block a user