delete tokens after 10 minutes

This commit is contained in:
Igor Hrenowitsch Propisnov 2024-09-09 09:18:15 +02:00
parent 786e4a59b8
commit 8a1089ce9d
5 changed files with 83 additions and 7 deletions

View File

@ -1,12 +1,16 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule'; import { Cron, CronExpression } from '@nestjs/schedule';
import { SessionService } from 'src/modules/session/services/session.service'; import { SessionService } from 'src/modules/session/services/session.service';
import { EmailVerificationService } from 'src/modules/verify-module/services/email-verification.service';
@Injectable() @Injectable()
export class ClearExpiredSessionsCron { export class ClearExpiredSessionsCron {
private readonly logger: Logger = new Logger(ClearExpiredSessionsCron.name); private readonly logger: Logger = new Logger(ClearExpiredSessionsCron.name);
public constructor(private readonly sessionService: SessionService) {} public constructor(
private readonly sessionService: SessionService,
private readonly emailVerificationService: EmailVerificationService
) {}
@Cron(CronExpression.EVERY_12_HOURS, { @Cron(CronExpression.EVERY_12_HOURS, {
name: 'Clear-Expired-Sessions', name: 'Clear-Expired-Sessions',
@ -17,4 +21,14 @@ export class ClearExpiredSessionsCron {
this.sessionService.deleteAllExpiredSessions(); this.sessionService.deleteAllExpiredSessions();
this.logger.log('-------------------------------------------'); this.logger.log('-------------------------------------------');
} }
@Cron(CronExpression.EVERY_10_MINUTES, {
name: 'Clear-Expired-Tokens',
timeZone: 'Europe/Berlin',
})
public handleClearExpiredTokens(): void {
this.logger.log('-Cronjob Executed: Delete-Expired-Tokens-');
this.emailVerificationService.deleteAllExpiredTokens();
this.logger.log('-------------------------------------------');
}
} }

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { EmailVerification } from 'src/entities'; import { EmailVerification } from 'src/entities';
import { Repository } from 'typeorm'; import { LessThan, MoreThan, Repository } from 'typeorm';
@Injectable() @Injectable()
export class EmailVerifyRepository { export class EmailVerifyRepository {
@ -24,6 +24,23 @@ export class EmailVerifyRepository {
}); });
} }
public async findValidVerification(
token: string,
email: string
): Promise<EmailVerification | undefined> {
const currentDate = new Date();
const tenMinutesAgo = new Date(currentDate.getTime() - 10 * 60 * 1000);
return await this.repository.findOne({
where: {
token,
email,
createdAt: MoreThan(tenMinutesAgo),
expiresAt: MoreThan(currentDate),
},
});
}
public async findByTokenAndEmail( public async findByTokenAndEmail(
token: string, token: string,
email: string email: string
@ -49,6 +66,14 @@ export class EmailVerifyRepository {
return this.repository.findOne({ where: { email } }); return this.repository.findOne({ where: { email } });
} }
public async deleteAllExpiredTokens(): Promise<void> {
const currentDate = new Date();
await this.repository.delete({
expiresAt: LessThan(currentDate),
});
}
public async deleteEmailVerificationByToken( public async deleteEmailVerificationByToken(
tokenToDelete: string tokenToDelete: string
): Promise<EmailVerification | null> { ): Promise<EmailVerification | null> {

View File

@ -4,7 +4,10 @@ import { Injectable } from '@nestjs/common';
import { EmailVerification } from 'src/entities'; import { EmailVerification } from 'src/entities';
import { SessionService } from 'src/modules/session/services/session.service'; import { SessionService } from 'src/modules/session/services/session.service';
import { SuccessDto, UriEncoderService } from 'src/shared'; import { SuccessDto, UriEncoderService } from 'src/shared';
import { InternalServerErrorException } from 'src/shared/exceptions'; import {
InternalServerErrorException,
TokenExpiredException,
} from 'src/shared/exceptions';
import { UserDataRepository } from '../../user-module/repositories/user-data.repository'; import { UserDataRepository } from '../../user-module/repositories/user-data.repository';
import { EmailVerifyRepository } from '../repositories'; import { EmailVerifyRepository } from '../repositories';
@ -59,11 +62,11 @@ export class EmailVerificationService {
): Promise<string> { ): Promise<string> {
try { try {
const verificationToken = await this.createVerificationToken(); const verificationToken = await this.createVerificationToken();
const expiration = new Date(Date.now() + 24 * 60 * 60 * 1000); const expiresAt = new Date(Date.now() + 10 * 60 * 1000);
await this.emailVerifyRepository.createEmailVerification( await this.emailVerifyRepository.createEmailVerification(
verificationToken, verificationToken,
expiration, expiresAt,
email, email,
null null
); );
@ -85,13 +88,23 @@ export class EmailVerificationService {
emailToVerify: string emailToVerify: string
): Promise<SuccessDto> { ): Promise<SuccessDto> {
try { try {
const findTokenAndEmail: EmailVerification = const findTokenAndEmail: EmailVerification | null =
await this.emailVerifyRepository.findByTokenAndEmail( await this.emailVerifyRepository.findValidVerification(
tokenToVerify, tokenToVerify,
emailToVerify emailToVerify
); );
if (!findTokenAndEmail) { if (!findTokenAndEmail) {
const expiredToken =
await this.emailVerifyRepository.findByTokenAndEmail(
tokenToVerify,
emailToVerify
);
if (expiredToken) {
throw new TokenExpiredException();
}
return { success: false }; return { success: false };
} }
@ -102,6 +115,9 @@ export class EmailVerificationService {
return { success: true }; return { success: true };
} catch (error) { } catch (error) {
if (error instanceof TokenExpiredException) {
throw error;
}
throw new InternalServerErrorException('EMAIL_VERIFICATION_ERROR', { throw new InternalServerErrorException('EMAIL_VERIFICATION_ERROR', {
message: 'An error occurred while verifying the email.', message: 'An error occurred while verifying the email.',
}); });
@ -128,6 +144,12 @@ export class EmailVerificationService {
} }
} }
async deleteAllExpiredTokens(): Promise<void> {
const currentDate = new Date();
await this.emailVerifyRepository.deleteAllExpiredTokens();
}
private async createVerificationToken(): Promise<string> { private async createVerificationToken(): Promise<string> {
const verifyToken = randomBytes(32).toString('hex'); const verifyToken = randomBytes(32).toString('hex');

View File

@ -2,3 +2,4 @@ export * from './conflict.exception';
export * from './forbidden.exception'; export * from './forbidden.exception';
export * from './internal-server-error.exception'; export * from './internal-server-error.exception';
export * from './not-found.exception'; export * from './not-found.exception';
export * from './token-expired.exception';

View File

@ -0,0 +1,14 @@
import { HttpStatus } from '@nestjs/common';
import { BaseException } from './base.exception';
export class TokenExpiredException extends BaseException {
public constructor(details?: unknown) {
super(
'The verification token has expired. Please request a new one.',
HttpStatus.BAD_REQUEST,
'TOKEN_EXPIRED',
details
);
}
}