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 { 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; } } }