fix(auth): one-time SSO and MFA tokens, permission changes effective on every replica
- Every JWT carries a jti. The SSO completion token and the MFA challenge (once a code was accepted) are recorded as used in ConsumedToken; a replayed request or copied cookie cannot open a second session. Used ids are cleaned up after they expire. - The token-validity check (active user, active role, password change) reads fresh on every request and refreshes the permission cache, so a deactivation or role change made on another replica applies to the very next request instead of up to 30 s later. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
fc6af8737b
commit
45efb08369
16 files changed
+166
-23
No files matched your search
@@ -0,0 +1,38 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { Prisma } from '../../generated/prisma';
|
||||
import { OneTimeTokenClaims } from './util/jwt-claims';
|
||||
|
||||
/**
|
||||
* Makes a short-lived helper token usable exactly once (SSO completion,
|
||||
* MFA challenge after a successful code): its id is recorded, and a
|
||||
* second presentation - a replayed request, a copied cookie - is refused.
|
||||
* Works across replicas because the record lives in the database.
|
||||
*/
|
||||
@Injectable()
|
||||
export class OneTimeTokenService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async consume(claims: OneTimeTokenClaims): Promise<void> {
|
||||
if (!claims.jti || !claims.exp) {
|
||||
throw new UnauthorizedException(
|
||||
'Anmeldung abgelaufen, bitte erneut anmelden',
|
||||
);
|
||||
}
|
||||
try {
|
||||
await this.prisma.consumedToken.create({
|
||||
data: { jti: claims.jti, expiresAt: new Date(claims.exp * 1000) },
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
if (
|
||||
error instanceof Prisma.PrismaClientKnownRequestError &&
|
||||
error.code === 'P2002'
|
||||
) {
|
||||
throw new UnauthorizedException(
|
||||
'Diese Anmeldung wurde bereits abgeschlossen, bitte erneut anmelden',
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user