diff --git a/src/captcha/captcha-challenge.service.ts b/src/captcha/captcha-challenge.service.ts index 7c0574c..e534015 100644 --- a/src/captcha/captcha-challenge.service.ts +++ b/src/captcha/captcha-challenge.service.ts @@ -32,12 +32,17 @@ export class CaptchaChallengeService { this.captchaService.normalizeAnswer(generated.text), ); + // expireAt is the MongoDB TTL sentinel. The TTL reaper fires every ~60 s, so + // setting it equal to expiresAt means Mongo can delete the document up to 60 s + // BEFORE the application-level expiry check runs — causing the intermittent + // "captchaId not found" error under load. Adding a 120 s grace buffer ensures + // the document is always present when verify() runs its own expiresAt check. await this.captchaChallengeDbService.create({ captchaId, answerHash, image: generated.image, expiresAt: generated.expiresAt, - expireAt: new Date(generated.expiresAt), + expireAt: new Date(generated.expiresAt + 120_000), usedAt: null, }); diff --git a/src/captcha/entities/schema/captcha-challenge.schema.ts b/src/captcha/entities/schema/captcha-challenge.schema.ts index ed413e8..5f35e73 100644 --- a/src/captcha/entities/schema/captcha-challenge.schema.ts +++ b/src/captcha/entities/schema/captcha-challenge.schema.ts @@ -10,8 +10,8 @@ export class CaptchaChallenge { @Prop({ required: true, unique: true, index: true }) captchaId: string; - @Prop() - answerHash?: string; + @Prop({ required: true }) + answerHash: string; @Prop({ required: true }) image: string;