More challenge logic

This commit is contained in:
Fang-Pen Lin
2025-11-07 09:19:24 -08:00
parent bc00710df3
commit 0613a49795
2 changed files with 36 additions and 23 deletions
@@ -2,6 +2,7 @@ import { Knex } from "knex";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TPkiAcmeAuthDALFactory } from "./pki-acme-auth-dal";
import { TPkiAcmeChallengeDALFactory } from "./pki-acme-challenge-dal";
import { AcmeAuthStatus, AcmeChallengeStatus, AcmeChallengeType } from "./pki-acme-schemas";
@@ -18,15 +19,16 @@ export const pkiAcmeChallengeServiceFactory = ({
}: TPkiAcmeChallengeServiceFactoryDep): TPkiAcmeChallengeServiceFactory => {
const appCfg = getConfig();
const validateChallengeResponse = async (challengeId: string): Promise<void> => {
const validateChallengeResponse = async (challengeId: string, tx?: Knex): Promise<void> => {
return await acmeChallengeDAL.transaction(async (tx: Knex) => {
logger.info({ challengeId }, "Validating ACME challenge response");
const challenge = await acmeChallengeDAL.findByIdForChallengeValidation(challengeId, tx);
if (!challenge) {
throw new NotFoundError({ message: "ACME challenge not found" });
}
if (challenge.status !== AcmeChallengeStatus.Processing) {
if (challenge.status !== AcmeChallengeStatus.Pending) {
throw new BadRequestError({
message: `ACME challenge is ${challenge.status} instead of ${AcmeChallengeStatus.Processing}`
message: `ACME challenge is ${challenge.status} instead of ${AcmeChallengeStatus.Pending}`
});
}
if (challenge.auth.expiresAt < new Date()) {
@@ -46,8 +48,8 @@ export const pkiAcmeChallengeServiceFactory = ({
const actualBaseUrl = appCfg.isAcmeDevelopmentMode
? `${baseUrl}:${appCfg.ACME_DEVELOPMENT_HTTP01_CHALLENGE_PORT}`
: baseUrl;
const challengeUrl = new URL(`/.well-known/acme-challenge/${challenge.auth.token}`, actualBaseUrl);
try {
// Notice: well, we are in a transaction, ideally we should not hold transaction and perform
// a long running operation for long time. But assuming we are not performing a tons of
// challenge validation at the same time, it should be fine.
@@ -68,6 +70,14 @@ export const pkiAcmeChallengeServiceFactory = ({
tx
);
await acmeAuthDAL.updateById(challenge.auth.account.id, { status: AcmeAuthStatus.Valid }, tx);
await acmeAuthDAL.updateById(challenge.auth.account.id, { status: AcmeAuthStatus.Valid }, tx);
} catch (error) {
logger.error(error, "Error validating ACME challenge response");
// TODO: we should retry the challenge validation a few times, but let's keep it simple for now
await acmeChallengeDAL.updateById(challengeId, { status: AcmeChallengeStatus.Invalid }, tx);
await acmeAuthDAL.updateById(challenge.auth.account.id, { status: AcmeAuthStatus.Invalid }, tx);
throw error;
}
});
};
@@ -631,6 +631,9 @@ export const pkiAcmeServiceFactory = ({
if (!challenge.auth.token) {
throw new AcmeServerInternalError({ message: "ACME challenge token is required" });
}
if (challenge.type !== AcmeChallengeType.HTTP_01) {
throw new BadRequestError({ message: "Only HTTP-01 challenges are supported for now" });
}
const updatedChallenge = await acmeChallengeDAL.updateById(
challengeId,
{ status: AcmeChallengeStatus.Pending },