import { Request as ExpressRequest, Response as ExpressResponse } from "express"; import { Option, tryDo } from "@shared/common/async_utils"; import { rpcClient } from "src/utils/rpc"; import got from "got"; import { logger } from "src/utils/logging"; import { ENV } from "src/utils/environment"; export interface VerifyInfo { captchaSitekey: string; userVerified: boolean; needsCaptcha: boolean; } export interface VerifyRequest { captchaResponse: string; } interface HCaptchaResponse { success: boolean; "error-codes"?: string[]; } const VERIFY_URL = "https://hcaptcha.com/siteverify"; type GetResult = Promise>; export const get = async (req: ExpressRequest, res: ExpressResponse): GetResult => { if (!req.session?.userId) { return res.json({ captchaSitekey: ENV.HCAPTCHA_SITEKEY, userVerified: true, }); } const verifiedResult = await tryDo(rpcClient.userVerified({ userId: req.session.userId })); let verified = true; if (!verifiedResult.ok) { logger.error("Failed to check verified state: %s", verifiedResult.error); } else { verified = verifiedResult.result.verified; logger.verbose("User %s (%s) verified: %s", req.session.username, req.session.userId, verified); } return res.json({ captchaSitekey: ENV.HCAPTCHA_SITEKEY, userVerified: verified, needsCaptcha: ENV.HCAPTCHA_ENABLED === "TRUE", }); }; type PostResult = Promise>>; export const post = async (req: ExpressRequest, res: ExpressResponse): PostResult => { const needVerify = ENV.HCAPTCHA_ENABLED === "TRUE"; if (!req.session?.userId) { return res.json({ ok: false, error: "Not logged in, please log in", }); } if (needVerify) { const hasToken = (body: unknown): body is VerifyRequest => body instanceof Object && (body as VerifyRequest).captchaResponse !== undefined; if (needVerify && !hasToken(req.body)) { return res.json({ ok: false, error: "No user token provided, please try again", }); } const response = await tryDo(got(VERIFY_URL, { method: "post", responseType: "json", form: { secret: ENV.HCAPTCHA_SECRET, response: req.body.captchaResponse, }, })); if (!response.ok) { logger.error("Failed to hCaptcha user %s: %s", req.session.userId, response.error); return res.json({ ok: false, error: "Failed to verify hCaptcha response. Please try again.", }); } const captchaResponse = response.result.body; if (!captchaResponse.success) { const errors = captchaResponse["error-codes"] ?? []; logger.error("Failed hCaptcha verify on user %s. Got errors: %s", req.session.userId, errors.join(";")); return res.json({ ok: false, error: "Failed to verify hCaptcha response. Please try again.", }); } } const verifyResponse = await rpcClient.verifyUser({ userId: req.session.userId }); if (!verifyResponse.ok) { logger.error("Failed to verify user %s (%s)", req.session.userId, req.session.username); return res.json({ ok: false, error: "Failed to verify your account. Please try again or contact server owners.", }); } return res.json({ ok: true, }); };